mirror of
https://github.com/jlengrand/engine.git
synced 2026-03-10 08:11:21 +00:00
chore: simplify error handling
chore: improve log messages for the user chore: make clear differentiation between internal and user errors
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
use chrono::Utc;
|
||||
|
||||
use qovery_engine::build_platform::local_docker::LocalDocker;
|
||||
use qovery_engine::cloud_provider::aws::AWS;
|
||||
use qovery_engine::cloud_provider::aws::kubernetes::{EKS, Options};
|
||||
use qovery_engine::cloud_provider::aws::kubernetes::node::Node;
|
||||
use qovery_engine::cloud_provider::aws::kubernetes::{Options, EKS};
|
||||
use qovery_engine::cloud_provider::aws::AWS;
|
||||
use qovery_engine::cloud_provider::TerraformStateCredentials;
|
||||
use qovery_engine::container_registry::ecr::ECR;
|
||||
use qovery_engine::dns_provider::cloudflare::Cloudflare;
|
||||
use qovery_engine::engine::Engine;
|
||||
use qovery_engine::error::ConfigurationError;
|
||||
use qovery_engine::error::EngineError;
|
||||
use qovery_engine::models::{
|
||||
Action, Application, Context, Environment, EnvironmentAction, GitCredentials, Kind,
|
||||
};
|
||||
@@ -82,10 +82,10 @@ fn main() {
|
||||
let session = match engine.session() {
|
||||
Ok(session) => session,
|
||||
Err(config_error) => match config_error {
|
||||
ConfigurationError::BuildPlatform(_) => panic!("build platform config error"),
|
||||
ConfigurationError::ContainerRegistry(_) => panic!("container registry config error"),
|
||||
ConfigurationError::CloudProvider(_) => panic!("cloud provider config error"),
|
||||
ConfigurationError::DnsProvider(_) => panic!("dns provider config error"),
|
||||
EngineError::BuildPlatform(_) => panic!("build platform config error"),
|
||||
EngineError::ContainerRegistry(_) => panic!("container registry config error"),
|
||||
EngineError::CloudProvider(_) => panic!("cloud provider config error"),
|
||||
EngineError::DnsProvider(_) => panic!("dns provider config error"),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use chrono::Utc;
|
||||
|
||||
use qovery_engine::build_platform::local_docker::LocalDocker;
|
||||
use qovery_engine::cloud_provider::aws::AWS;
|
||||
use qovery_engine::cloud_provider::aws::kubernetes::{EKS, Options};
|
||||
use qovery_engine::cloud_provider::aws::kubernetes::node::Node;
|
||||
use qovery_engine::cloud_provider::aws::kubernetes::{Options, EKS};
|
||||
use qovery_engine::cloud_provider::aws::router::Router;
|
||||
use qovery_engine::cloud_provider::aws::AWS;
|
||||
use qovery_engine::cloud_provider::TerraformStateCredentials;
|
||||
use qovery_engine::container_registry::ecr::ECR;
|
||||
use qovery_engine::dns_provider::cloudflare::Cloudflare;
|
||||
use qovery_engine::engine::Engine;
|
||||
use qovery_engine::error::ConfigurationError;
|
||||
use qovery_engine::error::EngineError;
|
||||
use qovery_engine::models::{
|
||||
Action, Application, Context, Environment, EnvironmentAction, GitCredentials, Kind,
|
||||
};
|
||||
@@ -83,10 +83,10 @@ fn main() {
|
||||
let session = match engine.session() {
|
||||
Ok(session) => session,
|
||||
Err(config_error) => match config_error {
|
||||
ConfigurationError::BuildPlatform(_) => panic!("build platform config error"),
|
||||
ConfigurationError::ContainerRegistry(_) => panic!("container registry config error"),
|
||||
ConfigurationError::CloudProvider(_) => panic!("cloud provider config error"),
|
||||
ConfigurationError::DnsProvider(_) => panic!("dns provider config error"),
|
||||
EngineError::BuildPlatform(_) => panic!("build platform config error"),
|
||||
EngineError::ContainerRegistry(_) => panic!("container registry config error"),
|
||||
EngineError::CloudProvider(_) => panic!("cloud provider config error"),
|
||||
EngineError::DnsProvider(_) => panic!("dns provider config error"),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#[derive(Debug)]
|
||||
pub enum BuildPlatformError {
|
||||
Unexpected(String),
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::build_platform::error::BuildPlatformError;
|
||||
use crate::build_platform::{Build, BuildError, BuildPlatform, BuildResult, Image, Kind};
|
||||
use git2::Error;
|
||||
|
||||
use crate::build_platform::{Build, BuildPlatform, BuildResult, Image, Kind};
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
|
||||
use crate::fs::workspace_directory;
|
||||
use crate::git::checkout_submodules;
|
||||
use crate::models::{
|
||||
Context, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressListener,
|
||||
ProgressScope,
|
||||
};
|
||||
use crate::transaction::CommitError::BuildImage;
|
||||
use crate::{cmd, git};
|
||||
|
||||
/// use Docker in local
|
||||
@@ -30,7 +31,7 @@ impl LocalDocker {
|
||||
}
|
||||
}
|
||||
|
||||
fn image_does_exist(&self, image: &Image) -> Result<bool, BuildError> {
|
||||
fn image_does_exist(&self, image: &Image) -> Result<bool, EngineError> {
|
||||
let envs = match self.context.docker_tcp_socket() {
|
||||
Some(tcp_socket) => vec![("DOCKER_HOST", tcp_socket.as_str())],
|
||||
None => vec![],
|
||||
@@ -66,10 +67,11 @@ impl BuildPlatform for LocalDocker {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Result<(), BuildPlatformError> {
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
if !crate::cmd::utilities::does_binary_exist("docker") {
|
||||
return Err(BuildPlatformError::Unexpected(
|
||||
"docker binary not found".to_string(),
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
String::from("docker binary not found"),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -80,7 +82,7 @@ impl BuildPlatform for LocalDocker {
|
||||
self.listeners.push(listener);
|
||||
}
|
||||
|
||||
fn build(&self, build: Build, force_build: bool) -> Result<BuildResult, BuildError> {
|
||||
fn build(&self, build: Build, force_build: bool) -> Result<BuildResult, EngineError> {
|
||||
info!("LocalDocker.build() called for {}", self.name());
|
||||
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
@@ -111,8 +113,14 @@ impl BuildPlatform for LocalDocker {
|
||||
match git_clone {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
error! {"Error while trying to clone repository {}", build.git_repository.url}
|
||||
return Err(BuildError::Git(err));
|
||||
let message = format!(
|
||||
"Error while cloning repository {}. Error: {:?}",
|
||||
&build.git_repository.url, err
|
||||
);
|
||||
|
||||
error!("{}", message);
|
||||
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,12 +129,32 @@ impl BuildPlatform for LocalDocker {
|
||||
let commit_id = &build.git_repository.commit_id;
|
||||
match git::checkout(&repo, &commit_id, build.git_repository.url.as_str()) {
|
||||
Ok(_) => {}
|
||||
Err(err) => return Err(BuildError::Git(err)),
|
||||
Err(err) => {
|
||||
let message = format!(
|
||||
"Error while git checkout repository {} with commit id {}. Error: {:?}",
|
||||
&build.git_repository.url, commit_id, err
|
||||
);
|
||||
|
||||
error!("{}", message);
|
||||
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, message));
|
||||
}
|
||||
}
|
||||
|
||||
// git checkout submodules
|
||||
let _ = checkout_submodules(&repo);
|
||||
// TODO what if we can't checkout submodules? Today we ignore it
|
||||
match checkout_submodules(repo) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
let message = format!(
|
||||
"Error while checkout submodules from repository {}. Error: {:?}",
|
||||
&build.git_repository.url, err
|
||||
);
|
||||
|
||||
error!("{}", message);
|
||||
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, message));
|
||||
}
|
||||
}
|
||||
|
||||
let into_dir_docker_style = format!("{}/.", into_dir.as_str());
|
||||
|
||||
@@ -140,11 +168,14 @@ impl BuildPlatform for LocalDocker {
|
||||
|
||||
match Path::new(dockerfile_complete_path.as_str()).exists() {
|
||||
false => {
|
||||
error!(
|
||||
let message = format!(
|
||||
"Unable to find Dockerfile path {}",
|
||||
dockerfile_complete_path.as_str()
|
||||
);
|
||||
return Err(BuildError::Error);
|
||||
|
||||
error!("{}", &message);
|
||||
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, message));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -220,7 +251,19 @@ impl BuildPlatform for LocalDocker {
|
||||
|
||||
match exit_status {
|
||||
Ok(_) => {}
|
||||
Err(_) => return Err(BuildError::Error),
|
||||
Err(err) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::User(
|
||||
"It looks like your Dockerfile is wrong. Did you consider building \
|
||||
your container locally using `qovery run` or `docker build`?",
|
||||
),
|
||||
format!(
|
||||
"error while building container image {}. Error: {:?}",
|
||||
self.name_with_id(),
|
||||
err
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
listeners_helper.start_in_progress(ProgressInfo::new(
|
||||
@@ -228,27 +271,34 @@ impl BuildPlatform for LocalDocker {
|
||||
id: build.image.application_id.clone(),
|
||||
},
|
||||
ProgressLevel::Info,
|
||||
Some("build is done ✔"),
|
||||
Some(format!(
|
||||
"container build is done for {} ✔",
|
||||
self.name_with_id()
|
||||
)),
|
||||
self.context.execution_id(),
|
||||
));
|
||||
|
||||
Ok(BuildResult { build })
|
||||
}
|
||||
|
||||
fn build_error(&self, build: Build) -> Result<BuildResult, BuildError> {
|
||||
fn build_error(&self, build: Build) -> Result<BuildResult, EngineError> {
|
||||
warn!("LocalDocker.build_error() called for {}", self.name());
|
||||
|
||||
let listener_helper = ListenersHelper::new(&self.listeners);
|
||||
|
||||
// FIXME
|
||||
let message = String::from("something goes wrong (not implemented)");
|
||||
|
||||
listener_helper.error(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
id: build.image.application_id,
|
||||
},
|
||||
ProgressLevel::Error,
|
||||
Some("something goes wrong (not implemented)"),
|
||||
Some(message.as_str()),
|
||||
self.context.execution_id(),
|
||||
));
|
||||
|
||||
// FIXME
|
||||
Err(BuildError::Error)
|
||||
Err(self.engine_error(EngineErrorCause::Internal, message))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@ use std::rc::Rc;
|
||||
use git2::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::build_platform::error::BuildPlatformError;
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
|
||||
use crate::git::Credentials;
|
||||
use crate::models::{Context, ProgressListener};
|
||||
|
||||
pub mod error;
|
||||
pub mod local_docker;
|
||||
|
||||
pub trait BuildPlatform {
|
||||
@@ -15,10 +14,24 @@ pub trait BuildPlatform {
|
||||
fn kind(&self) -> Kind;
|
||||
fn id(&self) -> &str;
|
||||
fn name(&self) -> &str;
|
||||
fn is_valid(&self) -> Result<(), BuildPlatformError>;
|
||||
fn name_with_id(&self) -> String {
|
||||
format!("{} ({})", self.name(), self.id())
|
||||
}
|
||||
fn is_valid(&self) -> Result<(), EngineError>;
|
||||
fn add_listener(&mut self, listener: Rc<Box<dyn ProgressListener>>);
|
||||
fn build(&self, build: Build, force_build: bool) -> Result<BuildResult, BuildError>;
|
||||
fn build_error(&self, build: Build) -> Result<BuildResult, BuildError>;
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Build {
|
||||
@@ -62,12 +75,6 @@ pub struct BuildResult {
|
||||
pub build: Build,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BuildError {
|
||||
Git(Error),
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum Kind {
|
||||
|
||||
@@ -6,11 +6,14 @@ use crate::cloud_provider::aws::{common, AWS};
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::service::{
|
||||
Action, Application as CApplication, Create, Delete, Pause, Service, ServiceError, ServiceType,
|
||||
Action, Application as CApplication, Create, Delete, Pause, Service, ServiceType,
|
||||
StatelessService,
|
||||
};
|
||||
use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
|
||||
use crate::error::{
|
||||
from_simple_error_to_engine_error, EngineError, EngineErrorCause, EngineErrorScope,
|
||||
};
|
||||
use crate::models::Context;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
@@ -126,7 +129,7 @@ impl Application {
|
||||
context
|
||||
}
|
||||
|
||||
fn delete(&self, target: &DeploymentTarget, is_error: bool) -> Result<(), ServiceError> {
|
||||
fn delete(&self, target: &DeploymentTarget, is_error: bool) -> Result<(), EngineError> {
|
||||
let (kubernetes, environment) = match target {
|
||||
DeploymentTarget::ManagedServices(k, env) => (*k, *env),
|
||||
DeploymentTarget::SelfHosted(k, env) => (*k, *env),
|
||||
@@ -137,20 +140,28 @@ impl Application {
|
||||
let selector = format!("app={}", self.name());
|
||||
|
||||
if is_error {
|
||||
let _ = common::get_stateless_resource_information(
|
||||
kubernetes,
|
||||
environment,
|
||||
workspace_dir.as_str(),
|
||||
selector.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::get_stateless_resource_information(
|
||||
kubernetes,
|
||||
environment,
|
||||
workspace_dir.as_str(),
|
||||
selector.as_str(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
// clean the resource
|
||||
let _ = common::do_stateless_service_cleanup(
|
||||
kubernetes,
|
||||
environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::do_stateless_service_cleanup(
|
||||
kubernetes,
|
||||
environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -212,7 +223,7 @@ impl Service for Application {
|
||||
}
|
||||
|
||||
impl Create for Application {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.application.on_create() called for {}", self.name());
|
||||
let (kubernetes, environment) = match target {
|
||||
DeploymentTarget::ManagedServices(k, env) => (*k, *env),
|
||||
@@ -229,10 +240,15 @@ impl Create for Application {
|
||||
let workspace_dir = self.workspace_directory();
|
||||
|
||||
let from_dir = format!("{}/aws/charts/q-application", self.context.lib_root_dir());
|
||||
let _ = crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
// render
|
||||
@@ -243,51 +259,67 @@ impl Create for Application {
|
||||
(AWS_SECRET_ACCESS_KEY, aws.secret_access_key.as_str()),
|
||||
];
|
||||
|
||||
let kubernetes_config_file_path = common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
// do exec helm upgrade and return the last deployment status
|
||||
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
let helm_history_row = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
),
|
||||
)?;
|
||||
|
||||
// check deployment status
|
||||
if helm_history_row.is_none() || !helm_history_row.unwrap().is_successfully_deployed() {
|
||||
// TODO get pod output by using kubectl and return it into the OnCreateFailed
|
||||
return Err(ServiceError::OnCreateFailed);
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::User(
|
||||
"Your application didn't start for some reason. \
|
||||
Are you sure your application is correctly running? You can give a try by running \
|
||||
locally `qovery run`. You can also check the application log from the web \
|
||||
interface or the CLI with `qovery log`",
|
||||
),
|
||||
format!("Application {} has failed to start ⤬", self.name_with_id()),
|
||||
));
|
||||
}
|
||||
|
||||
// check app status
|
||||
let selector = format!("app={}", self.name());
|
||||
|
||||
match crate::cmd::kubectl::kubectl_exec_is_pod_ready_with_retry(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
selector.as_str(),
|
||||
aws_credentials_envs,
|
||||
) {
|
||||
Ok(Some(true)) => {}
|
||||
_ => return Err(ServiceError::OnCreateFailed),
|
||||
}
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::kubectl::kubectl_exec_is_pod_ready_with_retry(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
selector.as_str(),
|
||||
aws_credentials_envs,
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_create_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!(
|
||||
"AWS.application.on_create_error() called for {}",
|
||||
self.name()
|
||||
@@ -311,29 +343,42 @@ impl Create for Application {
|
||||
(AWS_SECRET_ACCESS_KEY, aws.secret_access_key.as_str()),
|
||||
];
|
||||
|
||||
let kubernetes_config_file_path = common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
let helm_release_name = self.helm_release_name();
|
||||
|
||||
let history_rows = crate::cmd::helm::helm_exec_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
)?;
|
||||
if history_rows.len() == 1 {
|
||||
crate::cmd::helm::helm_exec_uninstall(
|
||||
let history_rows = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
),
|
||||
)?;
|
||||
|
||||
if history_rows.len() == 1 {
|
||||
from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_uninstall(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -341,16 +386,16 @@ impl Create for Application {
|
||||
}
|
||||
|
||||
impl Pause for Application {
|
||||
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.application.on_pause() called for {}", self.name());
|
||||
self.delete(target, false)
|
||||
}
|
||||
|
||||
fn on_pause_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_pause_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_pause_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!(
|
||||
"AWS.application.on_pause_error() called for {}",
|
||||
self.name()
|
||||
@@ -360,16 +405,16 @@ impl Pause for Application {
|
||||
}
|
||||
|
||||
impl Delete for Application {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.application.on_delete() called for {}", self.name());
|
||||
self.delete(target, false)
|
||||
}
|
||||
|
||||
fn on_delete_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_delete_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!(
|
||||
"AWS.application.on_delete_error() called for {}",
|
||||
self.name()
|
||||
|
||||
@@ -6,9 +6,11 @@ use rusoto_core::Region;
|
||||
use crate::cloud_provider::aws::AWS;
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::service::ServiceError;
|
||||
use crate::cmd::utilities::CmdError;
|
||||
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
|
||||
use crate::error::{EngineError, SimpleError};
|
||||
|
||||
pub type Logs = String;
|
||||
pub type Describe = String;
|
||||
|
||||
pub fn kubernetes_config_path(
|
||||
workspace_directory: &str,
|
||||
@@ -17,7 +19,7 @@ pub fn kubernetes_config_path(
|
||||
access_key_id: &str,
|
||||
secret_access_key: &str,
|
||||
region: &str,
|
||||
) -> Result<String, Error> {
|
||||
) -> Result<String, SimpleError> {
|
||||
let kubernetes_config_bucket_name = format!("qovery-kubeconfigs-{}", kubernetes_cluster_id);
|
||||
let kubernetes_config_object_key = format!("{}.yaml", kubernetes_cluster_id);
|
||||
|
||||
@@ -40,16 +42,13 @@ pub fn kubernetes_config_path(
|
||||
Ok(kubernetes_config_file_path)
|
||||
}
|
||||
|
||||
pub type Logs = String;
|
||||
pub type Describe = String;
|
||||
|
||||
/// show different output (kubectl describe, log..) for debug purpose
|
||||
pub fn get_stateless_resource_information(
|
||||
kubernetes: &dyn Kubernetes,
|
||||
environment: &Environment,
|
||||
workspace_dir: &str,
|
||||
selector: &str,
|
||||
) -> Result<(Describe, Logs), CmdError> {
|
||||
) -> Result<(Describe, Logs), SimpleError> {
|
||||
let aws = kubernetes
|
||||
.cloud_provider()
|
||||
.as_any()
|
||||
@@ -112,7 +111,7 @@ pub fn do_stateless_service_cleanup(
|
||||
environment: &Environment,
|
||||
workspace_dir: &str,
|
||||
helm_release_name: &str,
|
||||
) -> Result<(), ServiceError> {
|
||||
) -> Result<(), SimpleError> {
|
||||
let aws = kubernetes
|
||||
.cloud_provider()
|
||||
.as_any()
|
||||
|
||||
@@ -6,10 +6,13 @@ use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::service::{
|
||||
Action, Backup, Create, Database, DatabaseOptions, DatabaseType, Delete, Downgrade, Pause,
|
||||
Service, ServiceError, ServiceType, StatefulService, Upgrade,
|
||||
Service, ServiceType, StatefulService, Upgrade,
|
||||
};
|
||||
use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
|
||||
use crate::error::{
|
||||
from_simple_error_to_engine_error, EngineError, EngineErrorCause, EngineErrorScope,
|
||||
};
|
||||
use crate::models::Context;
|
||||
|
||||
pub struct MongoDB {
|
||||
@@ -79,12 +82,14 @@ impl MongoDB {
|
||||
.as_any()
|
||||
.downcast_ref::<AWS>()
|
||||
.expect("Could not downcast kubernetes.cloud_provider() to AWS");
|
||||
|
||||
// we need the kubernetes config file to store tfstates file in kube secrets
|
||||
let kubernetes_config_file_path = utilities::get_kubernetes_config_path(
|
||||
self.workspace_directory().as_str(),
|
||||
kubernetes,
|
||||
environment,
|
||||
);
|
||||
|
||||
match kubernetes_config_file_path {
|
||||
Ok(kube_config) => {
|
||||
context.insert("kubeconfig_path", &kube_config.as_str());
|
||||
@@ -96,8 +101,12 @@ impl MongoDB {
|
||||
|
||||
utilities::create_namespace(&environment.namespace(), kube_config.as_str(), aws);
|
||||
}
|
||||
Err(e) => error!("Failed to generate the kubernetes config file path: {}", e),
|
||||
Err(e) => error!(
|
||||
"Failed to generate the kubernetes config file path: {:?}",
|
||||
e
|
||||
),
|
||||
}
|
||||
|
||||
context.insert("namespace", environment.namespace());
|
||||
|
||||
context.insert("aws_access_key", &cp.access_key_id);
|
||||
@@ -122,7 +131,7 @@ impl MongoDB {
|
||||
context
|
||||
}
|
||||
|
||||
fn delete(&self, target: &DeploymentTarget, is_error: bool) -> Result<(), ServiceError> {
|
||||
fn delete(&self, target: &DeploymentTarget, is_error: bool) -> Result<(), EngineError> {
|
||||
let workspace_dir = self.workspace_directory();
|
||||
|
||||
match target {
|
||||
@@ -134,39 +143,58 @@ impl MongoDB {
|
||||
|
||||
let context = self.tera_context(*kubernetes, *environment);
|
||||
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/mongodb", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/mongodb", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
match crate::cmd::terraform::terraform_exec_with_init_validate_destroy(
|
||||
workspace_dir.as_str(),
|
||||
) {
|
||||
Ok(o) => {
|
||||
Ok(_) => {
|
||||
info!("Deleting secrets containing tfstates");
|
||||
utilities::delete_terraform_tfstate_secret(
|
||||
*kubernetes,
|
||||
@@ -174,8 +202,16 @@ impl MongoDB {
|
||||
self.workspace_directory().as_str(),
|
||||
);
|
||||
}
|
||||
//TODO: find a way to raise the error
|
||||
Err(e) => error!("Error while destroying infrastructure {}", e),
|
||||
Err(e) => {
|
||||
let message = format!(
|
||||
"Error while destroying infrastructure {}",
|
||||
e.message.unwrap_or("".into())
|
||||
);
|
||||
|
||||
error!("{}", message);
|
||||
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
DeploymentTarget::SelfHosted(kubernetes, environment) => {
|
||||
@@ -183,20 +219,28 @@ impl MongoDB {
|
||||
let selector = format!("app={}", self.name());
|
||||
|
||||
if is_error {
|
||||
let _ = common::get_stateless_resource_information(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
selector.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::get_stateless_resource_information(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
selector.as_str(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
// clean the resource
|
||||
let _ = common::do_stateless_service_cleanup(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::do_stateless_service_cleanup(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -252,7 +296,7 @@ impl Service for MongoDB {
|
||||
impl Database for MongoDB {}
|
||||
|
||||
impl Create for MongoDB {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.MongoDB.on_create() called for {}", self.name());
|
||||
|
||||
let workspace_dir = self.workspace_directory();
|
||||
@@ -264,30 +308,48 @@ impl Create for MongoDB {
|
||||
let context = self.tera_context(*kubernetes, *environment);
|
||||
let workspace_dir = self.workspace_directory();
|
||||
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/mongodb", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/mongodb", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
// deploy database + external DNS
|
||||
crate::cmd::terraform::terraform_exec_with_init_validate_plan_apply(
|
||||
workspace_dir.as_str(),
|
||||
false,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::terraform::terraform_exec_with_init_validate_plan_apply(
|
||||
workspace_dir.as_str(),
|
||||
false,
|
||||
),
|
||||
)?;
|
||||
}
|
||||
DeploymentTarget::SelfHosted(kubernetes, environment) => {
|
||||
@@ -301,21 +363,29 @@ impl Create for MongoDB {
|
||||
.downcast_ref::<AWS>()
|
||||
.unwrap();
|
||||
|
||||
let kubernetes_config_file_path = common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
let from_dir = format!("{}/common/services/mongodb", self.context.lib_root_dir());
|
||||
|
||||
let _ = crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
// render templates
|
||||
@@ -326,19 +396,26 @@ impl Create for MongoDB {
|
||||
];
|
||||
|
||||
// do exec helm upgrade and return the last deployment status
|
||||
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
let helm_history_row = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
),
|
||||
)?;
|
||||
|
||||
// check deployment status
|
||||
if helm_history_row.is_none()
|
||||
|| !helm_history_row.unwrap().is_successfully_deployed()
|
||||
{
|
||||
return Err(ServiceError::OnCreateFailed);
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
"MongoDB database fails to be deployed (before start)".into(),
|
||||
));
|
||||
}
|
||||
|
||||
// check app status
|
||||
@@ -351,7 +428,16 @@ impl Create for MongoDB {
|
||||
aws_credentials_envs,
|
||||
) {
|
||||
Ok(Some(true)) => {}
|
||||
_ => return Err(ServiceError::OnCreateFailed),
|
||||
_ => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"MongoDB database {} with id {} failed to start after several retries",
|
||||
self.name(),
|
||||
self.id()
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,11 +445,11 @@ impl Create for MongoDB {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_create_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!("AWS.MongoDB.on_create_error() called for {}", self.name());
|
||||
|
||||
self.delete(target, true)
|
||||
@@ -371,7 +457,7 @@ impl Create for MongoDB {
|
||||
}
|
||||
|
||||
impl Pause for MongoDB {
|
||||
fn on_pause(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.MongoDB.on_pause() called for {}", self.name());
|
||||
|
||||
// TODO how to pause production? - the goal is to reduce cost, but it is possible to pause a production env?
|
||||
@@ -380,11 +466,11 @@ impl Pause for MongoDB {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_pause_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_pause_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!("AWS.MongoDB.on_pause_error() called for {}", self.name());
|
||||
|
||||
// TODO what to do if there is a pause error?
|
||||
@@ -394,85 +480,85 @@ impl Pause for MongoDB {
|
||||
}
|
||||
|
||||
impl Delete for MongoDB {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.MongoDB.on_delete() called for {}", self.name());
|
||||
self.delete(target, false)
|
||||
}
|
||||
|
||||
fn on_delete_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_delete_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!("AWS.MongoDB.on_create_error() called for {}", self.name());
|
||||
self.delete(target, true)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::cloud_provider::service::Clone for MongoDB {
|
||||
fn on_clone(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_clone(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_clone_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_clone_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_clone_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_clone_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Upgrade for MongoDB {
|
||||
fn on_upgrade(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_upgrade(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_upgrade_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_upgrade_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_upgrade_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_upgrade_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Downgrade for MongoDB {
|
||||
fn on_downgrade(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_downgrade(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_downgrade_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_downgrade_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_downgrade_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_downgrade_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Backup for MongoDB {
|
||||
fn on_backup(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_backup(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_backup_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_backup_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_backup_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_backup_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_restore(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_restore(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_restore_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_restore_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_restore_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_restore_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,17 @@ use crate::cloud_provider::aws::{common, AWS};
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::service::{
|
||||
Action, Backup, Create, DatabaseOptions, DatabaseType, Delete, Downgrade, Pause, Service,
|
||||
ServiceError, ServiceType, StatefulService, Upgrade,
|
||||
Action, Backup, Create, Database, DatabaseOptions, DatabaseType, Delete, Downgrade, Pause,
|
||||
Service, ServiceType, StatefulService, Upgrade,
|
||||
};
|
||||
use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::cmd::kubectl::{
|
||||
kubectl_exec_create_namespace, kubectl_exec_delete_namespace, kubectl_exec_delete_secret,
|
||||
};
|
||||
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
|
||||
use crate::error::{
|
||||
from_simple_error_to_engine_error, EngineError, EngineErrorCause, EngineErrorScope,
|
||||
};
|
||||
use crate::models::Context;
|
||||
|
||||
pub struct MySQL {
|
||||
@@ -58,9 +61,11 @@ impl MySQL {
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
fn helm_release_name(&self) -> String {
|
||||
crate::string::cut(format!("mysql-{}", self.id()), 50)
|
||||
}
|
||||
|
||||
fn workspace_directory(&self) -> String {
|
||||
crate::fs::workspace_directory(
|
||||
self.context.workspace_root_dir(),
|
||||
@@ -68,20 +73,24 @@ impl MySQL {
|
||||
format!("databases/{}", self.name()),
|
||||
)
|
||||
}
|
||||
|
||||
fn tera_context(&self, kubernetes: &dyn Kubernetes, environment: &Environment) -> TeraContext {
|
||||
let mut context = self.default_tera_context(kubernetes, environment);
|
||||
|
||||
// FIXME: is there an other way than downcast a pointer?
|
||||
let cp = kubernetes
|
||||
.cloud_provider()
|
||||
.as_any()
|
||||
.downcast_ref::<AWS>()
|
||||
.expect("Could not downcast kubernetes.cloud_provider() to AWS");
|
||||
|
||||
// we need the kubernetes config file to store tfstates file in kube secrets
|
||||
let kubernetes_config_file_path = utilities::get_kubernetes_config_path(
|
||||
self.workspace_directory().as_str(),
|
||||
kubernetes,
|
||||
environment,
|
||||
);
|
||||
|
||||
match kubernetes_config_file_path {
|
||||
Ok(kube_config) => {
|
||||
context.insert("kubeconfig_path", &kube_config.as_str());
|
||||
@@ -93,8 +102,12 @@ impl MySQL {
|
||||
|
||||
utilities::create_namespace(&environment.namespace(), kube_config.as_str(), aws);
|
||||
}
|
||||
Err(e) => error!("Failed to generate the kubernetes config file path: {}", e),
|
||||
Err(e) => error!(
|
||||
"Failed to generate the kubernetes config file path: {:?}",
|
||||
e
|
||||
),
|
||||
}
|
||||
|
||||
context.insert("namespace", environment.namespace());
|
||||
|
||||
context.insert("aws_access_key", &cp.access_key_id);
|
||||
@@ -119,7 +132,7 @@ impl MySQL {
|
||||
context
|
||||
}
|
||||
|
||||
fn delete(&self, target: &DeploymentTarget, is_error: bool) -> Result<(), ServiceError> {
|
||||
fn delete(&self, target: &DeploymentTarget, is_error: bool) -> Result<(), EngineError> {
|
||||
let workspace_dir = self.workspace_directory();
|
||||
|
||||
match target {
|
||||
@@ -131,39 +144,58 @@ impl MySQL {
|
||||
|
||||
let context = self.tera_context(*kubernetes, *environment);
|
||||
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/mysql", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/mysql", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
match crate::cmd::terraform::terraform_exec_with_init_validate_destroy(
|
||||
workspace_dir.as_str(),
|
||||
) {
|
||||
Ok(o) => {
|
||||
Ok(_) => {
|
||||
info!("Deleting secrets containing tfstates");
|
||||
utilities::delete_terraform_tfstate_secret(
|
||||
*kubernetes,
|
||||
@@ -171,8 +203,16 @@ impl MySQL {
|
||||
self.workspace_directory().as_str(),
|
||||
);
|
||||
}
|
||||
//TODO: find a way to raise the error
|
||||
Err(e) => error!("Error while destroying infrastructure {}", e),
|
||||
Err(e) => {
|
||||
let message = format!(
|
||||
"Error while destroying infrastructure {}",
|
||||
e.message.unwrap_or("".into())
|
||||
);
|
||||
|
||||
error!("{}", message);
|
||||
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
DeploymentTarget::SelfHosted(kubernetes, environment) => {
|
||||
@@ -180,20 +220,28 @@ impl MySQL {
|
||||
let selector = format!("app={}", self.name());
|
||||
|
||||
if is_error {
|
||||
let _ = common::get_stateless_resource_information(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
selector.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::get_stateless_resource_information(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
selector.as_str(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
// clean the resource
|
||||
let _ = common::do_stateless_service_cleanup(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::do_stateless_service_cleanup(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -246,8 +294,10 @@ impl Service for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for MySQL {}
|
||||
|
||||
impl Create for MySQL {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
match target {
|
||||
DeploymentTarget::ManagedServices(kubernetes, environment) => {
|
||||
// use terraform
|
||||
@@ -256,29 +306,47 @@ impl Create for MySQL {
|
||||
|
||||
let workspace_dir = self.workspace_directory();
|
||||
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/mysql", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
crate::cmd::terraform::terraform_exec_with_init_validate_plan_apply(
|
||||
workspace_dir.as_str(),
|
||||
false,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/mysql", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::terraform::terraform_exec_with_init_validate_plan_apply(
|
||||
workspace_dir.as_str(),
|
||||
false,
|
||||
),
|
||||
)?;
|
||||
}
|
||||
DeploymentTarget::SelfHosted(kubernetes, environment) => {
|
||||
@@ -294,21 +362,29 @@ impl Create for MySQL {
|
||||
.downcast_ref::<AWS>()
|
||||
.unwrap();
|
||||
|
||||
let kubernetes_config_file_path = common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
let from_dir = format!("{}/common/services/mysql", self.context.lib_root_dir());
|
||||
|
||||
let _ = crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
// render templates
|
||||
@@ -319,19 +395,26 @@ impl Create for MySQL {
|
||||
];
|
||||
|
||||
// do exec helm upgrade and return the last deployment status
|
||||
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
let helm_history_row = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
),
|
||||
)?;
|
||||
|
||||
// check deployment status
|
||||
if helm_history_row.is_none()
|
||||
|| !helm_history_row.unwrap().is_successfully_deployed()
|
||||
{
|
||||
return Err(ServiceError::OnCreateFailed);
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
"MySQL database fails to be deployed (before start)".into(),
|
||||
));
|
||||
}
|
||||
|
||||
// check app status
|
||||
@@ -344,7 +427,16 @@ impl Create for MySQL {
|
||||
aws_credentials_envs,
|
||||
) {
|
||||
Ok(Some(true)) => {}
|
||||
_ => return Err(ServiceError::OnCreateFailed),
|
||||
_ => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"MySQL database {} with id {} failed to start after several retries",
|
||||
self.name(),
|
||||
self.id()
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,12 +444,12 @@ impl Create for MySQL {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_create_check(&self) -> Result<(), EngineError> {
|
||||
//FIXME : perform an actual check
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!("AWS.MySQL.on_create_error() called for {}", self.name());
|
||||
|
||||
self.delete(target, true)
|
||||
@@ -365,7 +457,7 @@ impl Create for MySQL {
|
||||
}
|
||||
|
||||
impl Pause for MySQL {
|
||||
fn on_pause(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.MySQL.on_pause() called for {}", self.name());
|
||||
|
||||
// TODO how to pause production? - the goal is to reduce cost, but it is possible to pause a production env?
|
||||
@@ -374,11 +466,11 @@ impl Pause for MySQL {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_pause_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_pause_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!("AWS.MySQL.on_pause_error() called for {}", self.name());
|
||||
|
||||
// TODO what to do if there is a pause error?
|
||||
@@ -388,85 +480,85 @@ impl Pause for MySQL {
|
||||
}
|
||||
|
||||
impl Delete for MySQL {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.MySQL.on_delete() called for {}", self.name());
|
||||
self.delete(target, false)
|
||||
}
|
||||
|
||||
fn on_delete_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_delete_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!("AWS.MySQL.on_create_error() called for {}", self.name());
|
||||
self.delete(target, true)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::cloud_provider::service::Clone for MySQL {
|
||||
fn on_clone(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_clone(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_clone_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_clone_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_clone_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_clone_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Upgrade for MySQL {
|
||||
fn on_upgrade(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_upgrade(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_upgrade_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_upgrade_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_upgrade_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_upgrade_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Downgrade for MySQL {
|
||||
fn on_downgrade(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_downgrade(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_downgrade_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_downgrade_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_downgrade_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_downgrade_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Backup for MySQL {
|
||||
fn on_backup(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_backup(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_backup_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_backup_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_backup_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_backup_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_restore(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_restore(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_restore_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_restore_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_restore_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_restore_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,13 @@ use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::service::{
|
||||
Action, Backup, Create, Database, DatabaseOptions, DatabaseType, Delete, Downgrade, Pause,
|
||||
Service, ServiceError, ServiceType, StatefulService, Upgrade,
|
||||
Service, ServiceType, StatefulService, Upgrade,
|
||||
};
|
||||
use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
|
||||
use crate::error::{
|
||||
from_simple_error_to_engine_error, EngineError, EngineErrorCause, EngineErrorScope,
|
||||
};
|
||||
use crate::models::Context;
|
||||
|
||||
pub struct PostgreSQL {
|
||||
@@ -69,18 +72,21 @@ impl PostgreSQL {
|
||||
|
||||
fn tera_context(&self, kubernetes: &dyn Kubernetes, environment: &Environment) -> TeraContext {
|
||||
let mut context = self.default_tera_context(kubernetes, environment);
|
||||
|
||||
// FIXME: is there an other way than downcast a pointer?
|
||||
let cp = kubernetes
|
||||
.cloud_provider()
|
||||
.as_any()
|
||||
.downcast_ref::<AWS>()
|
||||
.expect("Could not downcast kubernetes.cloud_provider() to AWS");
|
||||
|
||||
// we need the kubernetes config file to store tfstates file in kube secrets
|
||||
let kubernetes_config_file_path = utilities::get_kubernetes_config_path(
|
||||
self.workspace_directory().as_str(),
|
||||
kubernetes,
|
||||
environment,
|
||||
);
|
||||
|
||||
match kubernetes_config_file_path {
|
||||
Ok(kube_config) => {
|
||||
context.insert("kubeconfig_path", &kube_config.as_str());
|
||||
@@ -92,8 +98,12 @@ impl PostgreSQL {
|
||||
|
||||
utilities::create_namespace(&environment.namespace(), kube_config.as_str(), aws);
|
||||
}
|
||||
Err(e) => error!("Failed to generate the kubernetes config file path: {}", e),
|
||||
Err(e) => error!(
|
||||
"Failed to generate the kubernetes config file path: {:?}",
|
||||
e
|
||||
),
|
||||
}
|
||||
|
||||
context.insert("namespace", environment.namespace());
|
||||
|
||||
context.insert("aws_access_key", &cp.access_key_id);
|
||||
@@ -118,7 +128,7 @@ impl PostgreSQL {
|
||||
context
|
||||
}
|
||||
|
||||
fn delete(&self, target: &DeploymentTarget, is_error: bool) -> Result<(), ServiceError> {
|
||||
fn delete(&self, target: &DeploymentTarget, is_error: bool) -> Result<(), EngineError> {
|
||||
let workspace_dir = self.workspace_directory();
|
||||
|
||||
match target {
|
||||
@@ -130,39 +140,58 @@ impl PostgreSQL {
|
||||
|
||||
let context = self.tera_context(*kubernetes, *environment);
|
||||
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/postgresql", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/postgresql", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
match crate::cmd::terraform::terraform_exec_with_init_validate_destroy(
|
||||
workspace_dir.as_str(),
|
||||
) {
|
||||
Ok(o) => {
|
||||
Ok(_) => {
|
||||
info!("Deleting secrets containing tfstates");
|
||||
utilities::delete_terraform_tfstate_secret(
|
||||
*kubernetes,
|
||||
@@ -170,8 +199,16 @@ impl PostgreSQL {
|
||||
self.workspace_directory().as_str(),
|
||||
);
|
||||
}
|
||||
//TODO: find a way to raise the error
|
||||
Err(e) => error!("Error while destroying infrastructure {}", e),
|
||||
Err(e) => {
|
||||
let message = format!(
|
||||
"Error while destroying infrastructure {}",
|
||||
e.message.unwrap_or("".into())
|
||||
);
|
||||
|
||||
error!("{}", message);
|
||||
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
DeploymentTarget::SelfHosted(kubernetes, environment) => {
|
||||
@@ -179,20 +216,28 @@ impl PostgreSQL {
|
||||
let selector = format!("app={}", self.name());
|
||||
|
||||
if is_error {
|
||||
let _ = common::get_stateless_resource_information(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
selector.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::get_stateless_resource_information(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
selector.as_str(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
// clean the resource
|
||||
let _ = common::do_stateless_service_cleanup(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::do_stateless_service_cleanup(
|
||||
*kubernetes,
|
||||
*environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -248,7 +293,7 @@ impl Service for PostgreSQL {
|
||||
impl Database for PostgreSQL {}
|
||||
|
||||
impl Create for PostgreSQL {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.PostgreSQL.on_create() called for {}", self.name());
|
||||
|
||||
let workspace_dir = self.workspace_directory();
|
||||
@@ -261,29 +306,47 @@ impl Create for PostgreSQL {
|
||||
|
||||
let workspace_dir = self.workspace_directory();
|
||||
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/postgresql", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
)?;
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir()).as_str(),
|
||||
&workspace_dir,
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
crate::cmd::terraform::terraform_exec_with_init_validate_plan_apply(
|
||||
workspace_dir.as_str(),
|
||||
false,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!("{}/aws/services/postgresql", self.context.lib_root_dir()).as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
format!(
|
||||
"{}/aws/charts/external-name-svc",
|
||||
self.context.lib_root_dir()
|
||||
)
|
||||
.as_str(),
|
||||
format!("{}/{}", workspace_dir, "external-name-svc").as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::terraform::terraform_exec_with_init_validate_plan_apply(
|
||||
workspace_dir.as_str(),
|
||||
false,
|
||||
),
|
||||
)?;
|
||||
}
|
||||
DeploymentTarget::SelfHosted(kubernetes, environment) => {
|
||||
@@ -298,22 +361,30 @@ impl Create for PostgreSQL {
|
||||
.downcast_ref::<AWS>()
|
||||
.unwrap();
|
||||
|
||||
let kubernetes_config_file_path = common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
let from_dir =
|
||||
format!("{}/common/services/postgresql", self.context.lib_root_dir());
|
||||
|
||||
let _ = crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
// render templates
|
||||
@@ -324,19 +395,26 @@ impl Create for PostgreSQL {
|
||||
];
|
||||
|
||||
// do exec helm upgrade and return the last deployment status
|
||||
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
let helm_history_row = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
),
|
||||
)?;
|
||||
|
||||
// check deployment status
|
||||
if helm_history_row.is_none()
|
||||
|| !helm_history_row.unwrap().is_successfully_deployed()
|
||||
{
|
||||
return Err(ServiceError::OnCreateFailed);
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
"PostgreSQL database fails to be deployed (before start)".into(),
|
||||
));
|
||||
}
|
||||
|
||||
// check app status
|
||||
@@ -349,7 +427,16 @@ impl Create for PostgreSQL {
|
||||
aws_credentials_envs,
|
||||
) {
|
||||
Ok(Some(true)) => {}
|
||||
_ => return Err(ServiceError::OnCreateFailed),
|
||||
_ => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"PostgreSQL database {} with id {} failed to start after several retries",
|
||||
self.name(),
|
||||
self.id()
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,11 +444,11 @@ impl Create for PostgreSQL {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_create_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!(
|
||||
"AWS.PostgreSQL.on_create_error() called for {}",
|
||||
self.name()
|
||||
@@ -372,7 +459,7 @@ impl Create for PostgreSQL {
|
||||
}
|
||||
|
||||
impl Pause for PostgreSQL {
|
||||
fn on_pause(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.PostgreSQL.on_pause() called for {}", self.name());
|
||||
|
||||
// TODO how to pause production? - the goal is to reduce cost, but it is possible to pause a production env?
|
||||
@@ -381,11 +468,11 @@ impl Pause for PostgreSQL {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_pause_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_pause_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!("AWS.PostgreSQL.on_pause_error() called for {}", self.name());
|
||||
|
||||
// TODO what to do if there is a pause error?
|
||||
@@ -395,16 +482,16 @@ impl Pause for PostgreSQL {
|
||||
}
|
||||
|
||||
impl Delete for PostgreSQL {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.PostgreSQL.on_delete() called for {}", self.name());
|
||||
self.delete(target, false)
|
||||
}
|
||||
|
||||
fn on_delete_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_delete_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!(
|
||||
"AWS.PostgreSQL.on_create_error() called for {}",
|
||||
self.name()
|
||||
@@ -414,69 +501,69 @@ impl Delete for PostgreSQL {
|
||||
}
|
||||
|
||||
impl crate::cloud_provider::service::Clone for PostgreSQL {
|
||||
fn on_clone(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_clone(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_clone_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_clone_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_clone_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_clone_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Upgrade for PostgreSQL {
|
||||
fn on_upgrade(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_upgrade(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_upgrade_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_upgrade_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_upgrade_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_upgrade_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Downgrade for PostgreSQL {
|
||||
fn on_downgrade(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_downgrade(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_downgrade_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_downgrade_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_downgrade_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_downgrade_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Backup for PostgreSQL {
|
||||
fn on_backup(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_backup(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_backup_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_backup_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_backup_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_backup_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_restore(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_restore(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_restore_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_restore_check(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_restore_error(&self, _target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_restore_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,14 @@ use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cmd::kubectl::{kubectl_exec_create_namespace, kubectl_exec_delete_secret};
|
||||
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
|
||||
use crate::error::SimpleError;
|
||||
|
||||
// generate the kubernetes config path
|
||||
pub fn get_kubernetes_config_path(
|
||||
workspace: &str,
|
||||
kubernetes: &dyn Kubernetes,
|
||||
environment: &Environment,
|
||||
) -> Result<String, Error> {
|
||||
) -> Result<String, SimpleError> {
|
||||
let aws = kubernetes
|
||||
.cloud_provider()
|
||||
.as_any()
|
||||
@@ -40,7 +41,7 @@ pub fn delete_terraform_tfstate_secret(
|
||||
kubernetes: &dyn Kubernetes,
|
||||
environment: &Environment,
|
||||
workspace_dir: &str,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), SimpleError> {
|
||||
let aws = kubernetes
|
||||
.cloud_provider()
|
||||
.as_any()
|
||||
@@ -67,7 +68,10 @@ pub fn delete_terraform_tfstate_secret(
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to generate the kubernetes config file path: {}", e);
|
||||
error!(
|
||||
"Failed to generate the kubernetes config file path: {:?}",
|
||||
e
|
||||
);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ use crate::cloud_provider::aws::{common, AWS};
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::service::{
|
||||
Action, Application, Create, Delete, Pause, Service, ServiceError, ServiceType,
|
||||
StatelessService,
|
||||
Action, Application as AApplication, Create, Delete, ExternalService as EExternalService,
|
||||
Pause, Service, ServiceType, StatelessService,
|
||||
};
|
||||
use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
|
||||
use crate::error::{from_simple_error_to_engine_error, EngineError, EngineErrorCause};
|
||||
use crate::models::Context;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
@@ -92,7 +93,7 @@ impl ExternalService {
|
||||
context
|
||||
}
|
||||
|
||||
fn delete(&self, target: &DeploymentTarget, is_error: bool) -> Result<(), ServiceError> {
|
||||
fn delete(&self, target: &DeploymentTarget, is_error: bool) -> Result<(), EngineError> {
|
||||
let (kubernetes, environment) = match target {
|
||||
DeploymentTarget::ManagedServices(k, env) => (*k, *env),
|
||||
DeploymentTarget::SelfHosted(k, env) => (*k, *env),
|
||||
@@ -103,20 +104,28 @@ impl ExternalService {
|
||||
let selector = format!("app={}", self.name());
|
||||
|
||||
if is_error {
|
||||
let _ = common::get_stateless_resource_information(
|
||||
kubernetes,
|
||||
environment,
|
||||
workspace_dir.as_str(),
|
||||
selector.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
crate::cloud_provider::service::ExternalService::engine_error_scope(self),
|
||||
self.context.execution_id(),
|
||||
common::get_stateless_resource_information(
|
||||
kubernetes,
|
||||
environment,
|
||||
workspace_dir.as_str(),
|
||||
selector.as_str(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
// clean the resource
|
||||
let _ = common::do_stateless_service_cleanup(
|
||||
kubernetes,
|
||||
environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
crate::cloud_provider::service::ExternalService::engine_error_scope(self),
|
||||
self.context.execution_id(),
|
||||
common::do_stateless_service_cleanup(
|
||||
kubernetes,
|
||||
environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -180,7 +189,7 @@ impl Service for ExternalService {
|
||||
}
|
||||
|
||||
impl Create for ExternalService {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!(
|
||||
"AWS.external_service.on_create() called for {}",
|
||||
self.name()
|
||||
@@ -200,10 +209,14 @@ impl Create for ExternalService {
|
||||
let workspace_dir = self.workspace_directory();
|
||||
|
||||
let from_dir = format!("{}/common/services/q-job", self.context.lib_root_dir());
|
||||
let _ = crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
crate::cloud_provider::service::ExternalService::engine_error_scope(self),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
// render
|
||||
@@ -214,28 +227,41 @@ impl Create for ExternalService {
|
||||
(AWS_SECRET_ACCESS_KEY, aws.secret_access_key.as_str()),
|
||||
];
|
||||
|
||||
let kubernetes_config_file_path = common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
crate::cloud_provider::service::ExternalService::engine_error_scope(self),
|
||||
self.context.execution_id(),
|
||||
common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
// do exec helm upgrade and return the last deployment status
|
||||
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
let helm_history_row = from_simple_error_to_engine_error(
|
||||
crate::cloud_provider::service::ExternalService::engine_error_scope(self),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
),
|
||||
)?;
|
||||
|
||||
// check deployment status
|
||||
if helm_history_row.is_none() || !helm_history_row.unwrap().is_successfully_deployed() {
|
||||
// TODO get pod output by using kubectl and return it into the OnCreateFailed
|
||||
return Err(ServiceError::OnCreateFailed);
|
||||
return Err(crate::cloud_provider::service::ExternalService::engine_error(self, EngineErrorCause::User(
|
||||
"Your External Service didn't start for some reason. \
|
||||
Are you sure your External Service is correctly running? You can give a try by running \
|
||||
locally `docker run`. You can also check the External Service log from the web \
|
||||
interface or the CLI with `qovery log`",
|
||||
), format!("External Service {} has failed to start ⤬", self.name_with_id()),
|
||||
));
|
||||
}
|
||||
|
||||
// check job status
|
||||
@@ -246,17 +272,29 @@ impl Create for ExternalService {
|
||||
aws_credentials_envs,
|
||||
) {
|
||||
Ok(Some(true)) => {}
|
||||
_ => return Err(ServiceError::OnCreateFailed),
|
||||
_ => {
|
||||
return Err(
|
||||
crate::cloud_provider::service::ExternalService::engine_error(
|
||||
self,
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"External Service {} with id {} failed to start after several retries",
|
||||
self.name(),
|
||||
self.id()
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_create_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!(
|
||||
"AWS.external_service.on_create_error() called for {}",
|
||||
self.name()
|
||||
@@ -279,30 +317,42 @@ impl Create for ExternalService {
|
||||
(AWS_SECRET_ACCESS_KEY, aws.secret_access_key.as_str()),
|
||||
];
|
||||
|
||||
let kubernetes_config_file_path = common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
crate::cloud_provider::service::ExternalService::engine_error_scope(self),
|
||||
self.context.execution_id(),
|
||||
common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
let helm_release_name = self.helm_release_name();
|
||||
|
||||
let history_rows = crate::cmd::helm::helm_exec_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
)?;
|
||||
|
||||
if history_rows.len() == 1 {
|
||||
crate::cmd::helm::helm_exec_uninstall(
|
||||
let history_rows = from_simple_error_to_engine_error(
|
||||
crate::cloud_provider::service::ExternalService::engine_error_scope(self),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
),
|
||||
)?;
|
||||
|
||||
if history_rows.len() == 1 {
|
||||
from_simple_error_to_engine_error(
|
||||
crate::cloud_provider::service::ExternalService::engine_error_scope(self),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_uninstall(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
aws_credentials_envs.clone(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -311,16 +361,16 @@ impl Create for ExternalService {
|
||||
}
|
||||
|
||||
impl Pause for ExternalService {
|
||||
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.external_service.on_pause() called for {}", self.name());
|
||||
self.delete(target, false)
|
||||
}
|
||||
|
||||
fn on_pause_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_pause_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_pause_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!(
|
||||
"AWS.external_service.on_pause_error() called for {}",
|
||||
self.name()
|
||||
@@ -330,7 +380,7 @@ impl Pause for ExternalService {
|
||||
}
|
||||
|
||||
impl Delete for ExternalService {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!(
|
||||
"AWS.external_service.on_delete() called for {}",
|
||||
self.name()
|
||||
@@ -338,11 +388,11 @@ impl Delete for ExternalService {
|
||||
self.delete(target, false)
|
||||
}
|
||||
|
||||
fn on_delete_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_delete_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!(
|
||||
"AWS.external_service.on_delete_error() called for {}",
|
||||
self.name()
|
||||
|
||||
@@ -16,20 +16,20 @@ use crate::cloud_provider::aws::kubernetes::node::Node;
|
||||
use crate::cloud_provider::aws::{common, AWS};
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::{
|
||||
check_kubernetes_has_enough_resources_to_deploy_environment, Kind, Kubernetes, KubernetesError,
|
||||
KubernetesNode, Resources,
|
||||
check_kubernetes_has_enough_resources_to_deploy_environment, Kind, Kubernetes, KubernetesNode,
|
||||
Resources,
|
||||
};
|
||||
use crate::cloud_provider::service::{Service, ServiceType};
|
||||
use crate::cloud_provider::{CloudProvider, DeploymentTarget};
|
||||
use crate::cmd;
|
||||
use crate::cmd::helm::helm_uninstall_list;
|
||||
use crate::cmd::kubectl::{kubectl_exec_delete_namespace, kubectl_exec_get_all_namespaces};
|
||||
use crate::cmd::utilities::CmdError;
|
||||
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
|
||||
use crate::deletion_utilities::{get_firsts_namespaces_to_delete, get_qovery_managed_namespaces};
|
||||
use crate::dns_provider::cloudflare::Cloudflare;
|
||||
use crate::dns_provider::DnsProvider;
|
||||
use crate::dns_provider::Kind::CLOUDFLARE;
|
||||
use crate::error::{from_simple_error_to_engine_error, EngineError, EngineErrorCause};
|
||||
use crate::fs::workspace_directory;
|
||||
use crate::models::{
|
||||
Context, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressListener,
|
||||
@@ -348,7 +348,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
self.dns_provider
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Result<(), KubernetesError> {
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -360,20 +360,24 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
fn resources(&self, environment: &Environment) -> Result<Resources, KubernetesError> {
|
||||
fn resources(&self, environment: &Environment) -> Result<Resources, EngineError> {
|
||||
let aws = self
|
||||
.cloud_provider()
|
||||
.as_any()
|
||||
.downcast_ref::<AWS>()
|
||||
.unwrap();
|
||||
|
||||
let kubernetes_config_file_path = common::kubernetes_config_path(
|
||||
self.context.workspace_root_dir(),
|
||||
environment.organization_id.as_str(),
|
||||
&self.id,
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
self.region(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::kubernetes_config_path(
|
||||
self.context.workspace_root_dir(),
|
||||
environment.organization_id.as_str(),
|
||||
&self.id,
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
self.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
let aws_credentials_envs = vec![
|
||||
@@ -381,8 +385,11 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
(AWS_SECRET_ACCESS_KEY, aws.secret_access_key.as_str()),
|
||||
];
|
||||
|
||||
let nodes =
|
||||
cmd::kubectl::kubectl_exec_get_node(kubernetes_config_file_path, aws_credentials_envs)?;
|
||||
let nodes = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
cmd::kubectl::kubectl_exec_get_node(kubernetes_config_file_path, aws_credentials_envs),
|
||||
)?;
|
||||
|
||||
let mut resources = Resources {
|
||||
free_cpu: 0.0,
|
||||
@@ -413,7 +420,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
Ok(resources)
|
||||
}
|
||||
|
||||
fn on_create(&self) -> Result<(), KubernetesError> {
|
||||
fn on_create(&self) -> Result<(), EngineError> {
|
||||
info!("EKS.on_create() called for {}", self.name());
|
||||
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
@@ -439,57 +446,78 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
|
||||
// generate terraform files and copy them into temp dir
|
||||
let context = self.tera_context();
|
||||
let _ = crate::template::generate_and_copy_all_files_into_dir(
|
||||
self.template_directory.as_str(),
|
||||
temp_dir.as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
self.template_directory.as_str(),
|
||||
temp_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
// copy lib/common/bootstrap/charts directory (and sub directory) into the lib/aws/bootstrap/common/charts directory.
|
||||
// this is due to the required dependencies of lib/aws/bootstrap/*.tf files
|
||||
let common_charts_temp_dir = format!("{}/common/charts", temp_dir.as_str());
|
||||
let _ = crate::template::copy_non_template_files(
|
||||
format!("{}/common/bootstrap/charts", self.context.lib_root_dir()),
|
||||
common_charts_temp_dir.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::copy_non_template_files(
|
||||
format!("{}/common/bootstrap/charts", self.context.lib_root_dir()),
|
||||
common_charts_temp_dir.as_str(),
|
||||
),
|
||||
)?;
|
||||
|
||||
let _ = crate::cmd::terraform::terraform_exec_with_init_validate_plan_apply(
|
||||
temp_dir.as_str(),
|
||||
true,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::terraform::terraform_exec_with_init_validate_plan_apply(
|
||||
temp_dir.as_str(),
|
||||
true,
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_error(&self) -> Result<(), KubernetesError> {
|
||||
fn on_create_error(&self) -> Result<(), EngineError> {
|
||||
warn!("EKS.on_create_error() called for {}", self.name());
|
||||
// FIXME
|
||||
Err(KubernetesError::Error)
|
||||
Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"{} Kubernetes cluster rollback not implemented",
|
||||
self.name()
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn on_upgrade(&self) -> Result<(), KubernetesError> {
|
||||
fn on_upgrade(&self) -> Result<(), EngineError> {
|
||||
info!("EKS.on_upgrade() called for {}", self.name());
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_upgrade_error(&self) -> Result<(), KubernetesError> {
|
||||
fn on_upgrade_error(&self) -> Result<(), EngineError> {
|
||||
warn!("EKS.on_upgrade_error() called for {}", self.name());
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_downgrade(&self) -> Result<(), KubernetesError> {
|
||||
fn on_downgrade(&self) -> Result<(), EngineError> {
|
||||
info!("EKS.on_downgrade() called for {}", self.name());
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_downgrade_error(&self) -> Result<(), KubernetesError> {
|
||||
fn on_downgrade_error(&self) -> Result<(), EngineError> {
|
||||
warn!("EKS.on_downgrade_error() called for {}", self.name());
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_delete(&self) -> Result<(), KubernetesError> {
|
||||
fn on_delete(&self) -> Result<(), EngineError> {
|
||||
info!("EKS.on_delete() called for {}", self.name());
|
||||
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
|
||||
listeners_helper.delete_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Infrastructure {
|
||||
execution_id: self.context.execution_id().to_string(),
|
||||
@@ -511,19 +539,30 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
|
||||
// generate terraform files and copy them into temp dir
|
||||
let context = self.tera_context();
|
||||
let _ = crate::template::generate_and_copy_all_files_into_dir(
|
||||
self.template_directory.as_str(),
|
||||
temp_dir.as_str(),
|
||||
&context,
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
self.template_directory.as_str(),
|
||||
temp_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
// copy lib/common/bootstrap/charts directory (and sub directory) into the lib/aws/bootstrap/common/charts directory.
|
||||
// this is due to the required dependencies of lib/aws/bootstrap/*.tf files
|
||||
let common_charts_temp_dir = format!("{}/common/charts", temp_dir.as_str());
|
||||
let _ = crate::template::copy_non_template_files(
|
||||
format!("{}/common/bootstrap/charts", self.context.lib_root_dir()),
|
||||
common_charts_temp_dir.as_str(),
|
||||
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::copy_non_template_files(
|
||||
format!("{}/common/bootstrap/charts", self.context.lib_root_dir()),
|
||||
common_charts_temp_dir.as_str(),
|
||||
),
|
||||
)?;
|
||||
|
||||
let aws_credentials_envs = vec![
|
||||
(
|
||||
AWS_ACCESS_KEY_ID,
|
||||
@@ -535,14 +574,19 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
),
|
||||
];
|
||||
|
||||
let kubernetes_config_file_path = kubernetes_config_path(
|
||||
self.context.workspace_root_dir(),
|
||||
self.cloud_provider.organization_id.as_str(),
|
||||
self.id(),
|
||||
self.cloud_provider.access_key_id.as_str(),
|
||||
self.cloud_provider.secret_access_key.as_str(),
|
||||
self.region(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
kubernetes_config_path(
|
||||
self.context.workspace_root_dir(),
|
||||
self.cloud_provider.organization_id.as_str(),
|
||||
self.id(),
|
||||
self.cloud_provider.access_key_id.as_str(),
|
||||
self.cloud_provider.secret_access_key.as_str(),
|
||||
self.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
let all_namespaces = kubectl_exec_get_all_namespaces(
|
||||
kubernetes_config_file_path,
|
||||
aws_credentials_envs.clone(),
|
||||
@@ -553,15 +597,20 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
Ok(namespace_vec) => {
|
||||
let namespaces_as_str = namespace_vec.iter().map(std::ops::Deref::deref).collect();
|
||||
let namespaces_to_delete = get_firsts_namespaces_to_delete(namespaces_as_str);
|
||||
|
||||
info!("Deleting non Qovery Namespaces");
|
||||
for namespace_to_delete in namespaces_to_delete.iter() {
|
||||
let kubernetes_config_file_path0 = kubernetes_config_path(
|
||||
self.context.workspace_root_dir(),
|
||||
self.cloud_provider.organization_id.as_str(),
|
||||
self.id(),
|
||||
self.cloud_provider.access_key_id.as_str(),
|
||||
self.cloud_provider.secret_access_key.as_str(),
|
||||
self.region(),
|
||||
let kubernetes_config_file_path0 = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
kubernetes_config_path(
|
||||
self.context.workspace_root_dir(),
|
||||
self.cloud_provider.organization_id.as_str(),
|
||||
self.id(),
|
||||
self.cloud_provider.access_key_id.as_str(),
|
||||
self.cloud_provider.secret_access_key.as_str(),
|
||||
self.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
let deletion = cmd::kubectl::kubectl_exec_delete_namespace(
|
||||
@@ -569,9 +618,10 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
namespace_to_delete,
|
||||
aws_credentials_envs.clone(),
|
||||
);
|
||||
|
||||
match deletion {
|
||||
Ok(out) => info!("Namespace {} is deleted", namespace_to_delete),
|
||||
Err(e) => {
|
||||
Ok(_) => info!("Namespace {} is deleted", namespace_to_delete),
|
||||
Err(_) => {
|
||||
error!(
|
||||
"Can't delete the namespace {}, quiting now",
|
||||
namespace_to_delete
|
||||
@@ -580,17 +630,29 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => error!("Error while getting all namespaces {}", e),
|
||||
|
||||
Err(e) => error!(
|
||||
"Error while getting all namespaces for Kubernetes cluster {}: error {:?}",
|
||||
self.name_with_id(),
|
||||
e.message
|
||||
),
|
||||
}
|
||||
|
||||
info!("Deleting Qovery managed Namespaces");
|
||||
let kubernetes_config_file_path2 = kubernetes_config_path(
|
||||
self.context.workspace_root_dir(),
|
||||
self.cloud_provider.organization_id.as_str(),
|
||||
self.id(),
|
||||
self.cloud_provider.access_key_id.as_str(),
|
||||
self.cloud_provider.secret_access_key.as_str(),
|
||||
self.region(),
|
||||
|
||||
let kubernetes_config_file_path2 = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
kubernetes_config_path(
|
||||
self.context.workspace_root_dir(),
|
||||
self.cloud_provider.organization_id.as_str(),
|
||||
self.id(),
|
||||
self.cloud_provider.access_key_id.as_str(),
|
||||
self.cloud_provider.secret_access_key.as_str(),
|
||||
self.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
// TODO use label instead fixed names
|
||||
let mut qovery_namespaces = get_qovery_managed_namespaces();
|
||||
for qovery_namespace in qovery_namespaces.iter() {
|
||||
@@ -600,8 +662,8 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
aws_credentials_envs.clone(),
|
||||
);
|
||||
match deletion {
|
||||
Ok(out) => info!("Namespace {} is fully deleted", qovery_namespace),
|
||||
Err(e) => {
|
||||
Ok(_) => info!("Namespace {} is fully deleted", qovery_namespace),
|
||||
Err(_) => {
|
||||
error!(
|
||||
"Can't delete the namespace {}, quiting now",
|
||||
qovery_namespace
|
||||
@@ -609,6 +671,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("Delete all remaining deployed helm applications");
|
||||
|
||||
match cmd::helm::helm_list(&kubernetes_config_file_path2, aws_credentials_envs.clone()) {
|
||||
@@ -619,15 +682,20 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
aws_credentials_envs.clone(),
|
||||
);
|
||||
}
|
||||
Err(e) => error!("Unable to get helm list"),
|
||||
Err(_) => error!("Unable to get helm list"),
|
||||
}
|
||||
|
||||
info!("Running Terraform destroy");
|
||||
let terraform_result =
|
||||
cmd::terraform::terraform_exec_with_init_validate_destroy(temp_dir.as_str())?;
|
||||
let terraform_result = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
cmd::terraform::terraform_exec_with_init_validate_destroy(temp_dir.as_str()),
|
||||
);
|
||||
|
||||
// we should delete the bucket containing the kubeconfig after
|
||||
// to prevent to loose connection from terraform to kube cluster
|
||||
match terraform_result {
|
||||
() => {
|
||||
Ok(_) => {
|
||||
info!("Deleting S3 Bucket containing Kubeconfig");
|
||||
let s3_kubeconfig_bucket = get_s3_kubeconfig_bucket_name(self.id.clone());
|
||||
let _region = Region::from_str(self.region()).unwrap();
|
||||
@@ -645,7 +713,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_delete_error(&self) -> Result<(), KubernetesError> {
|
||||
fn on_delete_error(&self) -> Result<(), EngineError> {
|
||||
warn!("EKS.on_delete_error() called for {}", self.name());
|
||||
|
||||
// FIXME What should we do if something goes wrong while deleting the cluster?
|
||||
@@ -653,7 +721,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deploy_environment(&self, environment: &Environment) -> Result<(), KubernetesError> {
|
||||
fn deploy_environment(&self, environment: &Environment) -> Result<(), EngineError> {
|
||||
info!("EKS.deploy_environment() called for {}", self.name());
|
||||
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
@@ -706,7 +774,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
self.context.execution_id(),
|
||||
));
|
||||
|
||||
return Err(KubernetesError::Deploy(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {
|
||||
listeners_helper.start_in_progress(ProgressInfo::new(
|
||||
@@ -761,7 +829,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
self.context.execution_id(),
|
||||
));
|
||||
|
||||
return Err(KubernetesError::Deploy(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {
|
||||
listeners_helper.start_in_progress(ProgressInfo::new(
|
||||
@@ -814,7 +882,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
self.context.execution_id(),
|
||||
));
|
||||
|
||||
return Err(KubernetesError::Deploy(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {
|
||||
listeners_helper.started(ProgressInfo::new(
|
||||
@@ -866,7 +934,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
self.context.execution_id(),
|
||||
));
|
||||
|
||||
return Err(KubernetesError::Deploy(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {
|
||||
listeners_helper.start_in_progress(ProgressInfo::new(
|
||||
@@ -886,7 +954,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deploy_environment_error(&self, environment: &Environment) -> Result<(), KubernetesError> {
|
||||
fn deploy_environment_error(&self, environment: &Environment) -> Result<(), EngineError> {
|
||||
warn!("EKS.deploy_environment_error() called for {}", self.name());
|
||||
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
@@ -947,7 +1015,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
self.context.execution_id(),
|
||||
));
|
||||
|
||||
return Err(KubernetesError::Deploy(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {
|
||||
listeners_helper.start_in_progress(ProgressInfo::new(
|
||||
@@ -1002,7 +1070,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
self.context.execution_id(),
|
||||
));
|
||||
|
||||
return Err(KubernetesError::Deploy(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {
|
||||
listeners_helper.start_in_progress(ProgressInfo::new(
|
||||
@@ -1022,7 +1090,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pause_environment(&self, environment: &Environment) -> Result<(), KubernetesError> {
|
||||
fn pause_environment(&self, environment: &Environment) -> Result<(), EngineError> {
|
||||
info!("EKS.pause_environment() called for {}", self.name());
|
||||
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
@@ -1047,7 +1115,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
err
|
||||
);
|
||||
|
||||
return Err(KubernetesError::Pause(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1066,7 +1134,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
err
|
||||
);
|
||||
|
||||
return Err(KubernetesError::Pause(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1083,7 +1151,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
err
|
||||
);
|
||||
|
||||
return Err(KubernetesError::Pause(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1099,7 +1167,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
err
|
||||
);
|
||||
|
||||
return Err(KubernetesError::Pause(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1108,12 +1176,12 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pause_environment_error(&self, _environment: &Environment) -> Result<(), KubernetesError> {
|
||||
fn pause_environment_error(&self, _environment: &Environment) -> Result<(), EngineError> {
|
||||
warn!("EKS.pause_environment_error() called for {}", self.name());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_environment(&self, environment: &Environment) -> Result<(), KubernetesError> {
|
||||
fn delete_environment(&self, environment: &Environment) -> Result<(), EngineError> {
|
||||
info!("EKS.delete_environment() called for {}", self.name());
|
||||
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
@@ -1139,7 +1207,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
err
|
||||
);
|
||||
|
||||
return Err(KubernetesError::Delete(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1158,7 +1226,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
err
|
||||
);
|
||||
|
||||
return Err(KubernetesError::Delete(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1175,7 +1243,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
err
|
||||
);
|
||||
|
||||
return Err(KubernetesError::Delete(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1191,7 +1259,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
err
|
||||
);
|
||||
|
||||
return Err(KubernetesError::Delete(err));
|
||||
return Err(err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1208,13 +1276,17 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
),
|
||||
];
|
||||
|
||||
let kubernetes_config_file_path = common::kubernetes_config_path(
|
||||
&self.context.workspace_root_dir(),
|
||||
&environment.organization_id.as_str(),
|
||||
&self.id,
|
||||
&self.cloud_provider.access_key_id.as_str(),
|
||||
&self.cloud_provider.secret_access_key.as_str(),
|
||||
&self.region.name(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::kubernetes_config_path(
|
||||
&self.context.workspace_root_dir(),
|
||||
&environment.organization_id.as_str(),
|
||||
&self.id,
|
||||
&self.cloud_provider.access_key_id.as_str(),
|
||||
&self.cloud_provider.secret_access_key.as_str(),
|
||||
&self.region.name(),
|
||||
),
|
||||
)?;
|
||||
|
||||
kubectl_exec_delete_namespace(
|
||||
@@ -1226,7 +1298,7 @@ impl<'a> Kubernetes for EKS<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_environment_error(&self, _environment: &Environment) -> Result<(), KubernetesError> {
|
||||
fn delete_environment_error(&self, _environment: &Environment) -> Result<(), EngineError> {
|
||||
warn!("EKS.delete_environment_error() called for {}", self.name());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ use rusoto_core::{Client, HttpClient, Region};
|
||||
use rusoto_credential::StaticProvider;
|
||||
use rusoto_sts::{GetCallerIdentityRequest, Sts, StsClient};
|
||||
|
||||
use crate::cloud_provider::{CloudProvider, CloudProviderError, Kind, TerraformStateCredentials};
|
||||
use crate::cloud_provider::{CloudProvider, EngineError, Kind, TerraformStateCredentials};
|
||||
use crate::error::EngineErrorCause;
|
||||
use crate::models::{Context, Listener, Listeners, ProgressListener};
|
||||
use crate::runtime::async_run;
|
||||
|
||||
@@ -85,13 +86,20 @@ impl CloudProvider for AWS {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Result<(), CloudProviderError> {
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
let client = StsClient::new_with_client(self.client(), Region::default());
|
||||
let s = async_run(client.get_caller_identity(GetCallerIdentityRequest::default()));
|
||||
|
||||
match s {
|
||||
Ok(_x) => Ok(()),
|
||||
Err(err) => Err(CloudProviderError::from(err)),
|
||||
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()))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
use crate::cloud_provider::aws::{common, AWS};
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::service::{
|
||||
Action, Create, Delete, Pause, Router as RRouter, Service, ServiceError, ServiceType,
|
||||
StatelessService,
|
||||
};
|
||||
use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
|
||||
use crate::models::{
|
||||
Context, Listeners, ListenersHelper, Metadata, ProgressInfo, ProgressLevel, ProgressScope,
|
||||
};
|
||||
use dns_lookup::lookup_host;
|
||||
use retry::delay::{Fibonacci, Fixed};
|
||||
use retry::OperationResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tera::Context as TeraContext;
|
||||
|
||||
use crate::cloud_provider::aws::{common, AWS};
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::service::{
|
||||
Action, Create, Delete, Pause, Router as RRouter, Service, ServiceType, StatelessService,
|
||||
};
|
||||
use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
|
||||
use crate::error::{
|
||||
from_simple_error_to_engine_error, EngineError, EngineErrorCause, SimpleError, SimpleErrorKind,
|
||||
};
|
||||
use crate::models::{
|
||||
Context, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope,
|
||||
};
|
||||
|
||||
pub struct Router {
|
||||
context: Context,
|
||||
id: String,
|
||||
@@ -219,7 +222,7 @@ impl Router {
|
||||
context
|
||||
}
|
||||
|
||||
fn delete(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let (kubernetes, environment) = match target {
|
||||
DeploymentTarget::ManagedServices(k, env) => (*k, *env),
|
||||
DeploymentTarget::SelfHosted(k, env) => (*k, *env),
|
||||
@@ -228,11 +231,15 @@ impl Router {
|
||||
let workspace_dir = self.workspace_directory();
|
||||
let helm_release_name = self.helm_release_name();
|
||||
|
||||
let _ = common::do_stateless_service_cleanup(
|
||||
kubernetes,
|
||||
environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::do_stateless_service_cleanup(
|
||||
kubernetes,
|
||||
environment,
|
||||
workspace_dir.as_str(),
|
||||
helm_release_name.as_str(),
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -282,7 +289,7 @@ impl Service for Router {
|
||||
}
|
||||
|
||||
impl crate::cloud_provider::service::Router for Router {
|
||||
fn check_domains(&self) -> Result<(), ServiceError> {
|
||||
fn check_domains(&self) -> Result<(), EngineError> {
|
||||
let check_result = retry::retry(Fibonacci::from_millis(3000).take(10), || {
|
||||
// TODO send information back to the core
|
||||
info!("check custom domain {}", self.default_domain.as_str());
|
||||
@@ -299,7 +306,15 @@ impl crate::cloud_provider::service::Router for Router {
|
||||
|
||||
match check_result {
|
||||
Ok(_) => {}
|
||||
Err(_) => return Err(ServiceError::CheckFailed),
|
||||
Err(_) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"domain {} is still not ready after several retries",
|
||||
self.default_domain.as_str()
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -309,7 +324,7 @@ impl crate::cloud_provider::service::Router for Router {
|
||||
impl StatelessService for Router {}
|
||||
|
||||
impl Create for Router {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.router.on_create() called for {}", self.name());
|
||||
let (kubernetes, environment) = match target {
|
||||
DeploymentTarget::ManagedServices(k, env) => (*k, *env),
|
||||
@@ -325,13 +340,17 @@ impl Create for Router {
|
||||
let workspace_dir = self.workspace_directory();
|
||||
let helm_release_name = self.helm_release_name();
|
||||
|
||||
let kubernetes_config_file_path = common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
let kubernetes_config_file_path = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
common::kubernetes_config_path(
|
||||
workspace_dir.as_str(),
|
||||
environment.organization_id.as_str(),
|
||||
kubernetes.id(),
|
||||
aws.access_key_id.as_str(),
|
||||
aws.secret_access_key.as_str(),
|
||||
kubernetes.region(),
|
||||
),
|
||||
)?;
|
||||
|
||||
// respect order - getting the context here and not before is mandatory
|
||||
@@ -349,33 +368,50 @@ impl Create for Router {
|
||||
);
|
||||
|
||||
let from_dir = format!("{}/common/chart_values", self.context.lib_root_dir());
|
||||
let _ = crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
into_dir.as_str(),
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
into_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
let _ = crate::template::copy_non_template_files(
|
||||
format!(
|
||||
"{}/common/charts/nginx-ingress",
|
||||
self.context().lib_root_dir()
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::copy_non_template_files(
|
||||
format!(
|
||||
"{}/common/charts/nginx-ingress",
|
||||
self.context().lib_root_dir()
|
||||
),
|
||||
into_dir.as_str(),
|
||||
),
|
||||
into_dir.as_str(),
|
||||
)?;
|
||||
|
||||
// do exec helm upgrade and return the last deployment status
|
||||
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history_with_override(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
format!("custom-{}", helm_release_name).as_str(),
|
||||
into_dir.as_str(),
|
||||
format!("{}/nginx-ingress.yaml", into_dir.as_str()).as_str(),
|
||||
self.aws_credentials_envs(aws).to_vec(),
|
||||
let helm_history_row = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_with_upgrade_history_with_override(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
format!("custom-{}", helm_release_name).as_str(),
|
||||
into_dir.as_str(),
|
||||
format!("{}/nginx-ingress.yaml", into_dir.as_str()).as_str(),
|
||||
self.aws_credentials_envs(aws).to_vec(),
|
||||
),
|
||||
)?;
|
||||
|
||||
// check deployment status
|
||||
if helm_history_row.is_none() || !helm_history_row.unwrap().is_successfully_deployed() {
|
||||
return Err(ServiceError::OnCreateFailed);
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
"Router has failed to be deployed".into(),
|
||||
));
|
||||
}
|
||||
|
||||
// waiting for the nlb, it should be deploy to get fqdn
|
||||
let external_ingress_hostname_custom_result =
|
||||
retry::retry(Fibonacci::from_millis(3000).take(10), || {
|
||||
@@ -390,6 +426,7 @@ impl Create for Router {
|
||||
.as_str(),
|
||||
self.aws_credentials_envs(aws).to_vec(),
|
||||
);
|
||||
|
||||
match external_ingress_hostname_custom {
|
||||
Ok(external_ingress_hostname_custom) => {
|
||||
OperationResult::Ok(external_ingress_hostname_custom)
|
||||
@@ -402,38 +439,51 @@ impl Create for Router {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
match external_ingress_hostname_custom_result {
|
||||
Ok(elb) => {
|
||||
//put it in the context
|
||||
context.insert("nlb_ingress_hostname", &elb);
|
||||
}
|
||||
Err(e) => error!("Error getting the NLB endpoint to be able to configure TLS"),
|
||||
Err(_) => error!("Error getting the NLB endpoint to be able to configure TLS"),
|
||||
}
|
||||
}
|
||||
|
||||
let from_dir = format!("{}/aws/charts/q-ingress-tls", self.context.lib_root_dir());
|
||||
let _ = crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
let _ = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::template::generate_and_copy_all_files_into_dir(
|
||||
from_dir.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
&context,
|
||||
),
|
||||
)?;
|
||||
|
||||
// do exec helm upgrade and return the last deployment status
|
||||
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
self.aws_credentials_envs(aws).to_vec(),
|
||||
let helm_history_row = from_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context.execution_id(),
|
||||
crate::cmd::helm::helm_exec_with_upgrade_history(
|
||||
kubernetes_config_file_path.as_str(),
|
||||
environment.namespace(),
|
||||
helm_release_name.as_str(),
|
||||
workspace_dir.as_str(),
|
||||
self.aws_credentials_envs(aws).to_vec(),
|
||||
),
|
||||
)?;
|
||||
|
||||
if helm_history_row.is_none() || !helm_history_row.unwrap().is_successfully_deployed() {
|
||||
return Err(ServiceError::OnCreateFailed);
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
"Router has failed to be deployed".into(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_create_check(&self) -> Result<(), EngineError> {
|
||||
let check_result = retry::retry(Fixed::from_millis(3000).take(60), || {
|
||||
let rs_ips = lookup_host(self.default_domain.as_str());
|
||||
match rs_ips {
|
||||
@@ -447,58 +497,63 @@ impl Create for Router {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
match check_result {
|
||||
Ok(out) => Ok(()),
|
||||
Err(e) => {
|
||||
error!("While checking the DNS propagation");
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => {
|
||||
let message = "While checking the DNS propagation";
|
||||
error!("{}", message);
|
||||
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
|
||||
listeners_helper.error(ProgressInfo::new(
|
||||
ProgressScope::Router {
|
||||
id: self.id.to_string(),
|
||||
id: self.id().into(),
|
||||
},
|
||||
ProgressLevel::Error,
|
||||
Some("DNS propagation goes wrong."),
|
||||
self.context.execution_id(),
|
||||
));
|
||||
Err(ServiceError::CheckFailed)
|
||||
|
||||
Err(self.engine_error(EngineErrorCause::Internal, message.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!("AWS.router.on_create_error() called for {}", self.name());
|
||||
self.delete(target)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for Router {
|
||||
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.router.on_pause() called for {}", self.name());
|
||||
self.delete(target)
|
||||
}
|
||||
|
||||
fn on_pause_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_pause_check(&self) -> Result<(), EngineError> {
|
||||
warn!("AWS.router.on_pause_error() called for {}", self.name());
|
||||
// TODO check resource has been cleaned?
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_pause_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_pause_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
self.delete(target)
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for Router {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
info!("AWS.router.on_delete() called for {}", self.name());
|
||||
self.delete(target)
|
||||
}
|
||||
|
||||
fn on_delete_check(&self) -> Result<(), ServiceError> {
|
||||
fn on_delete_check(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
warn!("AWS.router.on_delete_error() called for {}", self.name());
|
||||
self.delete(target)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ use std::rc::Rc;
|
||||
|
||||
use digitalocean::DigitalOcean;
|
||||
|
||||
use crate::cloud_provider::{CloudProvider, CloudProviderError, Kind, TerraformStateCredentials};
|
||||
use crate::cloud_provider::{CloudProvider, Kind, TerraformStateCredentials};
|
||||
use crate::error::EngineError;
|
||||
use crate::models::{Context, Listener, ProgressListener};
|
||||
|
||||
pub struct DO {
|
||||
@@ -49,7 +50,7 @@ impl CloudProvider for DO {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Result<(), CloudProviderError> {
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::cloud_provider::service::{ServiceError, StatefulService, StatelessService};
|
||||
use crate::cloud_provider::service::{StatefulService, StatelessService};
|
||||
use crate::error::EngineError;
|
||||
use crate::unit_conversion::cpu_string_to_float;
|
||||
|
||||
pub struct Environment {
|
||||
@@ -38,7 +39,7 @@ impl Environment {
|
||||
self.namespace.as_str()
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> Result<(), ServiceError> {
|
||||
pub fn is_valid(&self) -> Result<(), EngineError> {
|
||||
for service in self.stateful_services.iter() {
|
||||
match service.is_valid() {
|
||||
Err(err) => return Err(err),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::any::Any;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::cloud_provider::{CloudProvider, CloudProviderError, Kind, TerraformStateCredentials};
|
||||
use crate::cloud_provider::{CloudProvider, Kind, TerraformStateCredentials};
|
||||
use crate::error::EngineError;
|
||||
use crate::models::{Context, Listener, ProgressListener};
|
||||
|
||||
pub struct GCP {
|
||||
@@ -43,7 +43,7 @@ impl<'x> CloudProvider for GCP {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Result<(), CloudProviderError> {
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +1,54 @@
|
||||
use std::any::Any;
|
||||
use std::process::ExitStatus;
|
||||
use std::rc::Rc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::service::ServiceError;
|
||||
use crate::cloud_provider::CloudProvider;
|
||||
use crate::cmd::utilities::CmdError;
|
||||
use crate::dns_provider::DnsProvider;
|
||||
use crate::models::{Context, Listener, Listeners, ProgressListener};
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
|
||||
use crate::models::{Context, Listener, Listeners};
|
||||
|
||||
pub trait Kubernetes {
|
||||
fn context(&self) -> &Context;
|
||||
fn kind(&self) -> Kind;
|
||||
fn id(&self) -> &str;
|
||||
fn name(&self) -> &str;
|
||||
fn name_with_id(&self) -> String {
|
||||
format!("{} ({})", self.name(), self.id())
|
||||
}
|
||||
fn version(&self) -> &str;
|
||||
fn region(&self) -> &str;
|
||||
fn cloud_provider(&self) -> &dyn CloudProvider;
|
||||
fn dns_provider(&self) -> &dyn DnsProvider;
|
||||
fn is_valid(&self) -> Result<(), KubernetesError>;
|
||||
fn is_valid(&self) -> Result<(), EngineError>;
|
||||
fn add_listener(&mut self, listener: Listener);
|
||||
fn listeners(&self) -> &Listeners;
|
||||
fn resources(&self, environment: &Environment) -> Result<Resources, KubernetesError>;
|
||||
fn on_create(&self) -> Result<(), KubernetesError>;
|
||||
fn on_create_error(&self) -> Result<(), KubernetesError>;
|
||||
fn on_upgrade(&self) -> Result<(), KubernetesError>;
|
||||
fn on_upgrade_error(&self) -> Result<(), KubernetesError>;
|
||||
fn on_downgrade(&self) -> Result<(), KubernetesError>;
|
||||
fn on_downgrade_error(&self) -> Result<(), KubernetesError>;
|
||||
fn on_delete(&self) -> Result<(), KubernetesError>;
|
||||
fn on_delete_error(&self) -> Result<(), KubernetesError>;
|
||||
fn deploy_environment(&self, environment: &Environment) -> Result<(), KubernetesError>;
|
||||
fn deploy_environment_error(&self, environment: &Environment) -> Result<(), KubernetesError>;
|
||||
fn pause_environment(&self, environment: &Environment) -> Result<(), KubernetesError>;
|
||||
fn pause_environment_error(&self, environment: &Environment) -> Result<(), KubernetesError>;
|
||||
fn delete_environment(&self, environment: &Environment) -> Result<(), KubernetesError>;
|
||||
fn delete_environment_error(&self, environment: &Environment) -> Result<(), KubernetesError>;
|
||||
fn resources(&self, environment: &Environment) -> Result<Resources, EngineError>;
|
||||
fn on_create(&self) -> Result<(), EngineError>;
|
||||
fn on_create_error(&self) -> Result<(), EngineError>;
|
||||
fn on_upgrade(&self) -> Result<(), EngineError>;
|
||||
fn on_upgrade_error(&self) -> Result<(), EngineError>;
|
||||
fn on_downgrade(&self) -> Result<(), EngineError>;
|
||||
fn on_downgrade_error(&self) -> Result<(), EngineError>;
|
||||
fn on_delete(&self) -> Result<(), EngineError>;
|
||||
fn on_delete_error(&self) -> Result<(), EngineError>;
|
||||
fn deploy_environment(&self, environment: &Environment) -> Result<(), EngineError>;
|
||||
fn deploy_environment_error(&self, environment: &Environment) -> Result<(), EngineError>;
|
||||
fn pause_environment(&self, environment: &Environment) -> Result<(), EngineError>;
|
||||
fn pause_environment_error(&self, environment: &Environment) -> Result<(), EngineError>;
|
||||
fn delete_environment(&self, environment: &Environment) -> Result<(), EngineError>;
|
||||
fn delete_environment_error(&self, environment: &Environment) -> Result<(), EngineError>;
|
||||
fn engine_error_scope(&self) -> EngineErrorScope {
|
||||
EngineErrorScope::Kubernetes(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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait KubernetesNode {
|
||||
@@ -64,91 +74,59 @@ pub struct Resources {
|
||||
pub running_nodes: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum KubernetesError {
|
||||
Cmd(CmdError),
|
||||
Io(std::io::Error),
|
||||
Create(ExitStatus),
|
||||
Deploy(ServiceError),
|
||||
Pause(ServiceError),
|
||||
Delete(ServiceError),
|
||||
Error,
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for KubernetesError {
|
||||
fn from(error: std::io::Error) -> Self {
|
||||
KubernetesError::Io(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CmdError> for KubernetesError {
|
||||
fn from(error: CmdError) -> Self {
|
||||
KubernetesError::Cmd(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KubernetesError> for Option<ServiceError> {
|
||||
fn from(item: KubernetesError) -> Self {
|
||||
return match item {
|
||||
KubernetesError::Deploy(e) | KubernetesError::Pause(e) | KubernetesError::Delete(e) => {
|
||||
Option::from(e)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// check that there is enough CPU and RAM, and pods resources
|
||||
/// before starting to deploy stateful and stateless services
|
||||
pub fn check_kubernetes_has_enough_resources_to_deploy_environment(
|
||||
kubernetes: &dyn Kubernetes,
|
||||
environment: &Environment,
|
||||
) -> Result<(), KubernetesError> {
|
||||
) -> Result<(), EngineError> {
|
||||
let resources = kubernetes.resources(environment)?;
|
||||
let required_resources = environment.required_resources();
|
||||
|
||||
let cause = EngineErrorCause::User("Contact your Organization administrator and consider to \
|
||||
add one more node or upgrade your nodes configuration. If not possible, pause or delete unused environments");
|
||||
|
||||
if required_resources.cpu > resources.free_cpu
|
||||
&& required_resources.ram_in_mib > resources.free_ram_in_mib
|
||||
{
|
||||
// not enough cpu and ram to deploy environment
|
||||
return Err(KubernetesError::Deploy(ServiceError::NotEnoughResources(
|
||||
format!(
|
||||
"There is not enough CPU and RAM resources on the Kubernetes '{}' cluster. \
|
||||
let message = format!(
|
||||
"There is not enough CPU and RAM resources on the Kubernetes '{}' cluster. \
|
||||
{} CPU and {}mib RAM requested. \
|
||||
{} CPU and {}mib RAM available. \
|
||||
Consider to add one more node or upgrade your nodes configuration.",
|
||||
kubernetes.name(),
|
||||
required_resources.cpu,
|
||||
required_resources.ram_in_mib,
|
||||
resources.free_cpu,
|
||||
resources.free_ram_in_mib,
|
||||
),
|
||||
)));
|
||||
{} CPU and {}mib RAM available.",
|
||||
kubernetes.name(),
|
||||
required_resources.cpu,
|
||||
required_resources.ram_in_mib,
|
||||
resources.free_cpu,
|
||||
resources.free_ram_in_mib,
|
||||
);
|
||||
|
||||
return Err(kubernetes.engine_error(cause, message));
|
||||
} else if required_resources.cpu > resources.free_cpu {
|
||||
// not enough cpu to deploy environment
|
||||
return Err(KubernetesError::Deploy(ServiceError::NotEnoughResources(
|
||||
format!(
|
||||
"There is not enough free CPU on the Kubernetes '{}' cluster. \
|
||||
let message = format!(
|
||||
"There is not enough free CPU on the Kubernetes '{}' cluster. \
|
||||
{} CPU requested. {} CPU available. \
|
||||
Consider to add one more node or upgrade your nodes configuration.",
|
||||
kubernetes.name(),
|
||||
required_resources.cpu,
|
||||
resources.free_cpu,
|
||||
),
|
||||
)));
|
||||
kubernetes.name(),
|
||||
required_resources.cpu,
|
||||
resources.free_cpu,
|
||||
);
|
||||
|
||||
return Err(kubernetes.engine_error(cause, message));
|
||||
} else if required_resources.ram_in_mib > resources.free_ram_in_mib {
|
||||
// not enough ram to deploy environment
|
||||
return Err(KubernetesError::Deploy(ServiceError::NotEnoughResources(
|
||||
format!(
|
||||
"There is not enough free RAM on the Kubernetes cluster '{}'. \
|
||||
let message = format!(
|
||||
"There is not enough free RAM on the Kubernetes cluster '{}'. \
|
||||
{}mib RAM requested. \
|
||||
{}mib RAM available. \
|
||||
Consider to add one more node or upgrade your nodes configuration.",
|
||||
kubernetes.name(),
|
||||
required_resources.ram_in_mib,
|
||||
resources.free_ram_in_mib,
|
||||
),
|
||||
)));
|
||||
kubernetes.name(),
|
||||
required_resources.ram_in_mib,
|
||||
resources.free_ram_in_mib,
|
||||
);
|
||||
|
||||
return Err(kubernetes.engine_error(cause, message));
|
||||
}
|
||||
|
||||
let mut required_pods = environment.stateless_services.len() as u16;
|
||||
@@ -162,14 +140,14 @@ pub fn check_kubernetes_has_enough_resources_to_deploy_environment(
|
||||
|
||||
if required_pods > resources.free_pods {
|
||||
// not enough free pods on the cluster
|
||||
return Err(KubernetesError::Deploy(ServiceError::NotEnoughResources(
|
||||
format!(
|
||||
"There is not enough free Pods ({} required) on the Kubernetes cluster '{}'. \
|
||||
let message = format!(
|
||||
"There is not enough free Pods ({} required) on the Kubernetes cluster '{}'. \
|
||||
Consider to add one more node or upgrade your nodes configuration.",
|
||||
required_pods,
|
||||
kubernetes.name(),
|
||||
),
|
||||
)));
|
||||
required_pods,
|
||||
kubernetes.name(),
|
||||
);
|
||||
|
||||
return Err(kubernetes.engine_error(cause, message));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
|
||||
use crate::models::{Context, Listener, ProgressListener};
|
||||
|
||||
pub mod aws;
|
||||
@@ -21,50 +22,26 @@ pub trait CloudProvider {
|
||||
fn id(&self) -> &str;
|
||||
fn organization_id(&self) -> &str;
|
||||
fn name(&self) -> &str;
|
||||
fn is_valid(&self) -> Result<(), CloudProviderError>;
|
||||
fn name_with_id(&self) -> String {
|
||||
format!("{} ({})", self.name(), self.id())
|
||||
}
|
||||
fn is_valid(&self) -> Result<(), EngineError>;
|
||||
fn add_listener(&mut self, listener: Listener);
|
||||
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;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CloudProviderError {
|
||||
Credentials,
|
||||
Error(Box<dyn std::error::Error>),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<Box<dyn std::error::Error>> for CloudProviderError {
|
||||
fn from(error: Box<dyn std::error::Error>) -> Self {
|
||||
CloudProviderError::Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<RusotoError<E>> for CloudProviderError {
|
||||
fn from(error: RusotoError<E>) -> Self {
|
||||
match error {
|
||||
RusotoError::Credentials(_) => CloudProviderError::Credentials,
|
||||
RusotoError::Service(_) => CloudProviderError::Unknown,
|
||||
RusotoError::HttpDispatch(_) => CloudProviderError::Unknown,
|
||||
RusotoError::Validation(_) => CloudProviderError::Unknown,
|
||||
RusotoError::ParseError(_) => CloudProviderError::Unknown,
|
||||
RusotoError::Unknown(e) => {
|
||||
if e.status == 403 {
|
||||
CloudProviderError::Credentials
|
||||
} else {
|
||||
CloudProviderError::Unknown
|
||||
}
|
||||
}
|
||||
RusotoError::Blocking => CloudProviderError::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DeployError {
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub enum Kind {
|
||||
AWS,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::io::Error;
|
||||
use std::net::TcpStream;
|
||||
use std::process::id;
|
||||
|
||||
@@ -8,15 +7,17 @@ use crate::build_platform::Image;
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::cmd::utilities::CmdError;
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
|
||||
use crate::models::{Context, ProgressScope};
|
||||
use crate::transaction::CommitError;
|
||||
|
||||
pub trait Service {
|
||||
fn context(&self) -> &Context;
|
||||
fn service_type(&self) -> ServiceType;
|
||||
fn id(&self) -> &str;
|
||||
fn name(&self) -> &str;
|
||||
fn name_with_id(&self) -> String {
|
||||
format!("{} ({})", self.name(), self.id())
|
||||
}
|
||||
fn version(&self) -> &str;
|
||||
fn action(&self) -> &Action;
|
||||
fn private_port(&self) -> Option<u16>;
|
||||
@@ -35,13 +36,21 @@ pub trait Service {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Result<(), ServiceError> {
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
let binaries = ["kubectl", "helm", "terraform", "aws-iam-authenticator"];
|
||||
|
||||
for binary in binaries.iter() {
|
||||
if !crate::cmd::utilities::does_binary_exist(binary) {
|
||||
let err = format!("{} binary not found", binary);
|
||||
return Err(ServiceError::Unexpected(err));
|
||||
|
||||
let cause = EngineErrorCause::Internal;
|
||||
|
||||
return Err(EngineError::new(
|
||||
EngineErrorCause::Internal,
|
||||
EngineErrorScope::Engine,
|
||||
self.id(),
|
||||
Some(err),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +102,7 @@ pub trait Service {
|
||||
}
|
||||
|
||||
pub trait StatelessService: Service + Create + Pause + Delete {
|
||||
fn exec_action(&self, deployment_target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn exec_action(&self, deployment_target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
match self.action() {
|
||||
crate::cloud_provider::service::Action::Create => self.on_create(deployment_target),
|
||||
crate::cloud_provider::service::Action::Delete => self.on_delete(deployment_target),
|
||||
@@ -106,7 +115,7 @@ pub trait StatelessService: Service + Create + Pause + Delete {
|
||||
pub trait StatefulService:
|
||||
Service + Create + Pause + Delete + Backup + Clone + Upgrade + Downgrade
|
||||
{
|
||||
fn exec_action(&self, deployment_target: &DeploymentTarget) -> Result<(), ServiceError> {
|
||||
fn exec_action(&self, deployment_target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
match self.action() {
|
||||
crate::cloud_provider::service::Action::Create => self.on_create(deployment_target),
|
||||
crate::cloud_provider::service::Action::Delete => self.on_delete(deployment_target),
|
||||
@@ -119,59 +128,109 @@ pub trait StatefulService:
|
||||
pub trait Application: StatelessService {
|
||||
fn image(&self) -> &Image;
|
||||
fn set_image(&mut self, image: Image);
|
||||
fn engine_error_scope(&self) -> EngineErrorScope {
|
||||
EngineErrorScope::Application(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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExternalService: Application {}
|
||||
pub trait ExternalService: StatelessService {
|
||||
fn engine_error_scope(&self) -> EngineErrorScope {
|
||||
EngineErrorScope::ExternalService(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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Router: StatelessService {
|
||||
fn check_domains(&self) -> Result<(), ServiceError>;
|
||||
fn check_domains(&self) -> Result<(), EngineError>;
|
||||
fn engine_error_scope(&self) -> EngineErrorScope {
|
||||
EngineErrorScope::Router(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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Database: StatefulService {}
|
||||
pub trait Database: StatefulService {
|
||||
fn engine_error_scope(&self) -> EngineErrorScope {
|
||||
EngineErrorScope::Database(
|
||||
self.id().to_string(),
|
||||
self.service_type().name().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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Create {
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_create_check(&self) -> Result<(), ServiceError>;
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
fn on_create_check(&self) -> Result<(), EngineError>;
|
||||
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
}
|
||||
|
||||
pub trait Pause {
|
||||
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_pause_check(&self) -> Result<(), ServiceError>;
|
||||
fn on_pause_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
fn on_pause_check(&self) -> Result<(), EngineError>;
|
||||
fn on_pause_error(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
}
|
||||
|
||||
pub trait Delete {
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_delete_check(&self) -> Result<(), ServiceError>;
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
fn on_delete_check(&self) -> Result<(), EngineError>;
|
||||
fn on_delete_error(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
}
|
||||
|
||||
pub trait Backup {
|
||||
fn on_backup(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_backup_check(&self) -> Result<(), ServiceError>;
|
||||
fn on_backup_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_restore(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_restore_check(&self) -> Result<(), ServiceError>;
|
||||
fn on_restore_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_backup(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
fn on_backup_check(&self) -> Result<(), EngineError>;
|
||||
fn on_backup_error(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
fn on_restore(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
fn on_restore_check(&self) -> Result<(), EngineError>;
|
||||
fn on_restore_error(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
}
|
||||
|
||||
pub trait Clone {
|
||||
fn on_clone(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_clone_check(&self) -> Result<(), ServiceError>;
|
||||
fn on_clone_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_clone(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
fn on_clone_check(&self) -> Result<(), EngineError>;
|
||||
fn on_clone_error(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
}
|
||||
|
||||
pub trait Upgrade {
|
||||
fn on_upgrade(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_upgrade_check(&self) -> Result<(), ServiceError>;
|
||||
fn on_upgrade_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_upgrade(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
fn on_upgrade_check(&self) -> Result<(), EngineError>;
|
||||
fn on_upgrade_error(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
}
|
||||
|
||||
pub trait Downgrade {
|
||||
fn on_downgrade(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_downgrade_check(&self) -> Result<(), ServiceError>;
|
||||
fn on_downgrade_error(&self, target: &DeploymentTarget) -> Result<(), ServiceError>;
|
||||
fn on_downgrade(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
fn on_downgrade_check(&self) -> Result<(), EngineError>;
|
||||
fn on_downgrade_error(&self, target: &DeploymentTarget) -> Result<(), EngineError>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
@@ -212,44 +271,12 @@ impl<'a> ServiceType<'a> {
|
||||
match self {
|
||||
ServiceType::Application => "Application",
|
||||
ServiceType::ExternalService => "ExternalService",
|
||||
ServiceType::Database(_) => "Database",
|
||||
ServiceType::Database(db_type) => match db_type {
|
||||
DatabaseType::PostgreSQL(_) => "PostgreSQL database",
|
||||
DatabaseType::MongoDB(_) => "MongoDB database",
|
||||
DatabaseType::MySQL(_) => "MySQL database",
|
||||
},
|
||||
ServiceType::Router => "Router",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ServiceError {
|
||||
OnCreateFailed,
|
||||
CheckFailed,
|
||||
Cmd(CmdError),
|
||||
Io(Error),
|
||||
NotEnoughResources(String),
|
||||
Unexpected(String),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ServiceError {
|
||||
fn from(err: Error) -> Self {
|
||||
ServiceError::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CmdError> for ServiceError {
|
||||
fn from(err: CmdError) -> Self {
|
||||
ServiceError::Cmd(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommitError> for Option<ServiceError> {
|
||||
fn from(err: CommitError) -> Self {
|
||||
return match err {
|
||||
CommitError::DeleteEnvironment(e)
|
||||
| CommitError::PauseEnvironment(e)
|
||||
| CommitError::DeployEnvironment(e)
|
||||
| CommitError::DeleteKubernetes(e)
|
||||
| CommitError::CreateKubernetes(e) => Option::from(e),
|
||||
CommitError::NotValidService(e) => Option::Some(e),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,9 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::cmd::structs::{Helm, HelmHistoryRow};
|
||||
use crate::cmd::utilities::{exec_with_envs_and_output, CmdError};
|
||||
use crate::cmd::utilities::exec_with_envs_and_output;
|
||||
use crate::constants::{KUBECONFIG, TF_PLUGIN_CACHE_DIR};
|
||||
use crate::error::{SimpleError, SimpleErrorKind};
|
||||
|
||||
pub fn helm_exec_with_upgrade_history<P>(
|
||||
kubernetes_config: P,
|
||||
@@ -21,7 +22,7 @@ pub fn helm_exec_with_upgrade_history<P>(
|
||||
release_name: &str,
|
||||
chart_root_dir: P,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<Option<HelmHistoryRow>, CmdError>
|
||||
) -> Result<Option<HelmHistoryRow>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -63,7 +64,7 @@ pub fn helm_exec_upgrade<P>(
|
||||
release_name: &str,
|
||||
chart_root_dir: P,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -99,7 +100,7 @@ pub fn helm_exec_uninstall<P>(
|
||||
namespace: &str,
|
||||
release_name: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -129,7 +130,7 @@ pub fn helm_exec_history<P>(
|
||||
namespace: &str,
|
||||
release_name: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<Vec<HelmHistoryRow>, CmdError>
|
||||
) -> Result<Vec<HelmHistoryRow>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -178,7 +179,7 @@ pub fn helm_uninstall_list<P>(
|
||||
kubernetes_config: P,
|
||||
helmlist: Vec<String>,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<String, CmdError>
|
||||
) -> Result<String, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -217,7 +218,7 @@ pub fn helm_exec_upgrade_with_override_file<P>(
|
||||
chart_root_dir: P,
|
||||
override_file: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -259,7 +260,7 @@ pub fn helm_exec_with_upgrade_history_with_override<P>(
|
||||
chart_root_dir: P,
|
||||
override_file: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<Option<HelmHistoryRow>, CmdError>
|
||||
) -> Result<Option<HelmHistoryRow>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -296,7 +297,10 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub fn helm_list<P>(kubernetes_config: P, envs: Vec<(&str, &str)>) -> Result<Vec<String>, CmdError>
|
||||
pub fn helm_list<P>(
|
||||
kubernetes_config: P,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<Vec<String>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -330,14 +334,15 @@ where
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error while deserializing all helms names {}", e);
|
||||
return Err(CmdError::Io(Error::new(std::io::ErrorKind::InvalidData, e)));
|
||||
let message = format!("Error while deserializing all helms names {}", e);
|
||||
error!("{}", message.as_str());
|
||||
return Err(SimpleError::new(SimpleErrorKind::Other, Some(message)));
|
||||
}
|
||||
}
|
||||
Ok(helms_name)
|
||||
}
|
||||
|
||||
pub fn helm_exec(args: Vec<&str>, envs: Vec<(&str, &str)>) -> Result<(), CmdError> {
|
||||
pub fn helm_exec(args: Vec<&str>, envs: Vec<(&str, &str)>) -> Result<(), SimpleError> {
|
||||
helm_exec_with_output(
|
||||
args,
|
||||
envs,
|
||||
@@ -355,7 +360,7 @@ pub fn helm_exec_with_output<F, X>(
|
||||
envs: Vec<(&str, &str)>,
|
||||
stdout_output: F,
|
||||
stderr_output: X,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
F: FnMut(Result<String, Error>),
|
||||
X: FnMut(Result<String, Error>),
|
||||
@@ -373,7 +378,7 @@ pub fn kubectl_exec_with_output<F, X>(
|
||||
envs: Vec<(&str, &str)>,
|
||||
stdout_output: F,
|
||||
stderr_output: X,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
F: FnMut(Result<String, Error>),
|
||||
X: FnMut(Result<String, Error>),
|
||||
|
||||
@@ -15,15 +15,16 @@ use crate::cmd::structs::{
|
||||
Item, KubernetesJob, KubernetesList, KubernetesNode, KubernetesPod, KubernetesPodStatusPhase,
|
||||
KubernetesService,
|
||||
};
|
||||
use crate::cmd::utilities::{exec_with_envs_and_output, CmdError};
|
||||
use crate::cmd::utilities::exec_with_envs_and_output;
|
||||
use crate::constants::{KUBECONFIG, TF_PLUGIN_CACHE_DIR};
|
||||
use crate::error::{SimpleError, SimpleErrorKind};
|
||||
|
||||
pub fn kubectl_exec_with_output<F, X>(
|
||||
args: Vec<&str>,
|
||||
envs: Vec<(&str, &str)>,
|
||||
stdout_output: F,
|
||||
stderr_output: X,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
F: FnMut(Result<String, Error>),
|
||||
X: FnMut(Result<String, Error>),
|
||||
@@ -41,7 +42,7 @@ pub fn kubectl_exec_get_external_ingress_hostname<P>(
|
||||
namespace: &str,
|
||||
selector: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<Option<String>, CmdError>
|
||||
) -> Result<Option<String>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -74,10 +75,10 @@ where
|
||||
Err(err) => {
|
||||
error!("{:?}", err);
|
||||
error!("{}", output_string.as_str());
|
||||
return Err(CmdError::Io(Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
output_string,
|
||||
)));
|
||||
return Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some(output_string),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -115,7 +116,7 @@ pub fn kubectl_exec_is_pod_ready_with_retry<P>(
|
||||
namespace: &str,
|
||||
selector: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<Option<bool>, CmdError>
|
||||
) -> Result<Option<bool>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -148,7 +149,7 @@ where
|
||||
total_delay: _,
|
||||
tries: _,
|
||||
} => Ok(Some(false)),
|
||||
retry::Error::Internal(err) => Err(CmdError::Unexpected(err)),
|
||||
retry::Error::Internal(err) => Err(SimpleError::new(SimpleErrorKind::Other, Some(err))),
|
||||
},
|
||||
Ok(_) => Ok(Some(true)),
|
||||
}
|
||||
@@ -159,7 +160,7 @@ pub fn kubectl_exec_is_pod_ready<P>(
|
||||
namespace: &str,
|
||||
selector: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<Option<bool>, CmdError>
|
||||
) -> Result<Option<bool>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -189,10 +190,10 @@ where
|
||||
Err(err) => {
|
||||
error!("{:?}", err);
|
||||
error!("{}", output_string.as_str());
|
||||
return Err(CmdError::Io(Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
output_string,
|
||||
)));
|
||||
return Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some(output_string),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -223,7 +224,7 @@ pub fn kubectl_exec_is_job_ready_with_retry<P>(
|
||||
namespace: &str,
|
||||
job_name: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<Option<bool>, CmdError>
|
||||
) -> Result<Option<bool>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -256,7 +257,7 @@ where
|
||||
total_delay: _,
|
||||
tries: _,
|
||||
} => Ok(Some(false)),
|
||||
retry::Error::Internal(err) => Err(CmdError::Unexpected(err)),
|
||||
retry::Error::Internal(err) => Err(SimpleError::new(SimpleErrorKind::Other, Some(err))),
|
||||
},
|
||||
Ok(_) => Ok(Some(true)),
|
||||
}
|
||||
@@ -267,7 +268,7 @@ pub fn kubectl_exec_is_job_ready<P>(
|
||||
namespace: &str,
|
||||
job_name: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<Option<bool>, CmdError>
|
||||
) -> Result<Option<bool>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -296,10 +297,10 @@ where
|
||||
Err(err) => {
|
||||
error!("{:?}", err);
|
||||
error!("{}", output_string.as_str());
|
||||
return Err(CmdError::Io(Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
output_string,
|
||||
)));
|
||||
return Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some(output_string),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -314,7 +315,7 @@ pub fn kubectl_exec_create_namespace<P>(
|
||||
kubernetes_config: P,
|
||||
namespace: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -344,7 +345,7 @@ pub fn create_sample_secret_terraform_in_namespace<P>(
|
||||
kubernetes_config: P,
|
||||
namespace_to_override: &str,
|
||||
envs: &Vec<(&str, &str)>,
|
||||
) -> Result<String, CmdError>
|
||||
) -> Result<String, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -378,7 +379,7 @@ pub fn does_contain_terraform_tfstate<P>(
|
||||
kubernetes_config: P,
|
||||
namespace: &str,
|
||||
envs: &Vec<(&str, &str)>,
|
||||
) -> Result<bool, CmdError>
|
||||
) -> Result<bool, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -409,7 +410,7 @@ where
|
||||
pub fn kubectl_exec_get_all_namespaces<P>(
|
||||
kubernetes_config: P,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<Vec<String>, Error>
|
||||
) -> Result<Vec<String>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -441,11 +442,15 @@ where
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("While deserializing Kubernetes namespaces names {}", e);
|
||||
return Err(Error::from(CmdError::Io(Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
output_string,
|
||||
))));
|
||||
error!(
|
||||
"Error while deserializing Kubernetes namespaces names {}",
|
||||
e
|
||||
);
|
||||
|
||||
return Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some(output_string),
|
||||
));
|
||||
}
|
||||
};
|
||||
Ok(to_return)
|
||||
@@ -455,17 +460,17 @@ pub fn kubectl_exec_delete_namespace<P>(
|
||||
kubernetes_config: P,
|
||||
namespace: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
match does_contain_terraform_tfstate(&kubernetes_config, &namespace, &envs) {
|
||||
Ok(exist) => match exist {
|
||||
true => {
|
||||
return Err(CmdError::Io(Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Namespace contains terraform tfstates in secret, can't delete it !",
|
||||
)));
|
||||
return Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some("Namespace contains terraform tfstates in secret, can't delete it !"),
|
||||
));
|
||||
}
|
||||
false => info!(
|
||||
"Namespace {} doesn't contain any tfstates, able to delete it",
|
||||
@@ -474,7 +479,7 @@ where
|
||||
},
|
||||
Err(e) => warn!(
|
||||
"Unable to execute describe on secrets: {}. it may not exist anymore?",
|
||||
e
|
||||
e.message.unwrap_or("".into())
|
||||
),
|
||||
};
|
||||
|
||||
@@ -502,7 +507,7 @@ pub fn kubectl_exec_delete_secret<P>(
|
||||
kubernetes_config: P,
|
||||
secret: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -531,7 +536,7 @@ pub fn kubectl_exec_logs<P>(
|
||||
namespace: &str,
|
||||
selector: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<String, CmdError>
|
||||
) -> Result<String, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -561,7 +566,7 @@ pub fn kubectl_exec_describe_pod<P>(
|
||||
namespace: &str,
|
||||
selector: &str,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<String, CmdError>
|
||||
) -> Result<String, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -589,7 +594,7 @@ where
|
||||
pub fn kubectl_exec_get_node<P>(
|
||||
kubernetes_config: P,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<KubernetesList<KubernetesNode>, CmdError>
|
||||
) -> Result<KubernetesList<KubernetesNode>, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -619,10 +624,10 @@ where
|
||||
Err(err) => {
|
||||
error!("{:?}", err);
|
||||
error!("{}", output_string.as_str());
|
||||
return Err(CmdError::Io(Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
output_string,
|
||||
)));
|
||||
return Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some(output_string),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -11,13 +11,14 @@ use retry::OperationResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::cmd::utilities::{exec_with_envs_and_output, CmdError};
|
||||
use crate::cmd::utilities::exec_with_envs_and_output;
|
||||
use crate::constants::{KUBECONFIG, TF_PLUGIN_CACHE_DIR};
|
||||
use crate::error::SimpleError;
|
||||
|
||||
fn terraform_exec_with_init_validate(
|
||||
root_dir: &str,
|
||||
first_time_init_terraform: bool,
|
||||
) -> Result<(), CmdError> {
|
||||
) -> Result<(), SimpleError> {
|
||||
// terraform init
|
||||
let init_args = if first_time_init_terraform {
|
||||
vec!["init"]
|
||||
@@ -37,7 +38,7 @@ fn terraform_exec_with_init_validate(
|
||||
fn terraform_exec_with_init_validate_plan(
|
||||
root_dir: &str,
|
||||
first_time_init_terraform: bool,
|
||||
) -> Result<(), CmdError> {
|
||||
) -> Result<(), SimpleError> {
|
||||
// terraform init
|
||||
let init_args = if first_time_init_terraform {
|
||||
vec!["init"]
|
||||
@@ -60,7 +61,7 @@ fn terraform_exec_with_init_validate_plan(
|
||||
pub fn terraform_exec_with_init_validate_plan_apply(
|
||||
root_dir: &str,
|
||||
first_time_init_terraform: bool,
|
||||
) -> Result<(), CmdError> {
|
||||
) -> Result<(), SimpleError> {
|
||||
// terraform init and plan
|
||||
terraform_exec_with_init_validate_plan(root_dir, first_time_init_terraform);
|
||||
|
||||
@@ -70,7 +71,7 @@ pub fn terraform_exec_with_init_validate_plan_apply(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn terraform_exec_with_init_validate_destroy(root_dir: &str) -> Result<(), CmdError> {
|
||||
pub fn terraform_exec_with_init_validate_destroy(root_dir: &str) -> Result<(), SimpleError> {
|
||||
// terraform init and plan
|
||||
terraform_exec_with_init_validate(root_dir, false);
|
||||
|
||||
@@ -78,11 +79,11 @@ pub fn terraform_exec_with_init_validate_destroy(root_dir: &str) -> Result<(), C
|
||||
terraform_exec(root_dir, vec!["destroy", "-auto-approve"])
|
||||
}
|
||||
|
||||
pub fn terraform_exec(root_dir: &str, args: Vec<&str>) -> Result<(), CmdError> {
|
||||
pub fn terraform_exec(root_dir: &str, args: Vec<&str>) -> Result<(), SimpleError> {
|
||||
let home_dir = home_dir().expect("Could not find $HOME");
|
||||
let tf_plugin_cache_dir = format!("{}/.terraform.d/plugin-cache", home_dir.to_str().unwrap());
|
||||
|
||||
match exec_with_envs_and_output(
|
||||
exec_with_envs_and_output(
|
||||
format!("{} terraform", root_dir).as_str(),
|
||||
args,
|
||||
vec![(TF_PLUGIN_CACHE_DIR, tf_plugin_cache_dir.as_str())],
|
||||
@@ -92,10 +93,5 @@ pub fn terraform_exec(root_dir: &str, args: Vec<&str>) -> Result<(), CmdError> {
|
||||
|line: Result<String, std::io::Error>| {
|
||||
error!("{}", line.unwrap());
|
||||
},
|
||||
) {
|
||||
Err(err) => return Err(err),
|
||||
Ok(out) => Ok(out),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::constants::{KUBECONFIG, TF_PLUGIN_CACHE_DIR};
|
||||
use crate::error::{SimpleError, SimpleErrorKind};
|
||||
|
||||
fn command<P>(binary: P, args: Vec<&str>, envs: Option<Vec<(&str, &str)>>) -> Command
|
||||
where
|
||||
@@ -53,7 +54,7 @@ where
|
||||
cmd
|
||||
}
|
||||
|
||||
pub fn exec<P>(binary: P, args: Vec<&str>) -> Result<(), CmdError>
|
||||
pub fn exec<P>(binary: P, args: Vec<&str>) -> Result<(), SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -62,21 +63,24 @@ where
|
||||
|
||||
let exit_status = match command(binary, args, None).spawn().unwrap().wait() {
|
||||
Ok(x) => x,
|
||||
Err(err) => return Err(CmdError::Io(err)),
|
||||
Err(err) => return Err(SimpleError::from(err)),
|
||||
};
|
||||
|
||||
if exit_status.success() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(CmdError::Exec(exit_status))
|
||||
Err(SimpleError::new::<&str>(
|
||||
SimpleErrorKind::Command(exit_status),
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn exec_with_envs<P>(
|
||||
binary: P,
|
||||
args: Vec<&str>,
|
||||
envs: Vec<(&str, &str)>,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -85,14 +89,17 @@ where
|
||||
|
||||
let exit_status = match command(binary, args, Some(envs)).spawn().unwrap().wait() {
|
||||
Ok(x) => x,
|
||||
Err(err) => return Err(CmdError::Io(err)),
|
||||
Err(err) => return Err(SimpleError::from(err)),
|
||||
};
|
||||
|
||||
if exit_status.success() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(CmdError::Exec(exit_status))
|
||||
Err(SimpleError::new::<String>(
|
||||
SimpleErrorKind::Command(exit_status),
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
fn _with_output<F, X>(mut child: Child, mut stdout_output: F, mut stderr_output: X) -> Child
|
||||
@@ -118,7 +125,7 @@ pub fn exec_with_output<P, F, X>(
|
||||
args: Vec<&str>,
|
||||
stdout_output: F,
|
||||
stderr_output: X,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
F: FnMut(Result<String, Error>),
|
||||
@@ -135,14 +142,17 @@ where
|
||||
|
||||
let exit_status = match child.wait() {
|
||||
Ok(x) => x,
|
||||
Err(err) => return Err(CmdError::Io(err)),
|
||||
Err(err) => return Err(SimpleError::from(err)),
|
||||
};
|
||||
|
||||
if exit_status.success() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(CmdError::Exec(exit_status))
|
||||
Err(SimpleError::new(
|
||||
SimpleErrorKind::Command(exit_status),
|
||||
Some(command_string),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn exec_with_envs_and_output<P, F, X>(
|
||||
@@ -151,7 +161,7 @@ pub fn exec_with_envs_and_output<P, F, X>(
|
||||
envs: Vec<(&str, &str)>,
|
||||
stdout_output: F,
|
||||
stderr_output: X,
|
||||
) -> Result<(), CmdError>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
F: FnMut(Result<String, Error>),
|
||||
@@ -168,14 +178,17 @@ where
|
||||
|
||||
let exit_status = match child.wait() {
|
||||
Ok(x) => x,
|
||||
Err(err) => return Err(CmdError::Io(err)),
|
||||
Err(err) => return Err(SimpleError::from(err)),
|
||||
};
|
||||
|
||||
if exit_status.success() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(CmdError::Exec(exit_status))
|
||||
Err(SimpleError::new(
|
||||
SimpleErrorKind::Command(exit_status),
|
||||
Some(command_string),
|
||||
))
|
||||
}
|
||||
|
||||
// return the output of "binary_name" --version
|
||||
@@ -238,35 +251,3 @@ where
|
||||
args.join(" ")
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CmdError {
|
||||
Exec(ExitStatus),
|
||||
Io(Error),
|
||||
Unexpected(String),
|
||||
}
|
||||
|
||||
impl Display for CmdError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let s = match self {
|
||||
CmdError::Exec(status) => format!("CmdError: Exec({})", status),
|
||||
CmdError::Io(io) => format!("CmdError: IO: {}", io),
|
||||
CmdError::Unexpected(s) => format!("CmdError: Unexpected: {}", s),
|
||||
};
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CmdError {}
|
||||
|
||||
impl From<std::io::Error> for CmdError {
|
||||
fn from(err: Error) -> Self {
|
||||
CmdError::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CmdError> for std::io::Error {
|
||||
fn from(e: CmdError) -> Self {
|
||||
std::io::Error::new(std::io::ErrorKind::Other, e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,8 @@ use std::rc::Rc;
|
||||
|
||||
use crate::build_platform::Image;
|
||||
use crate::cmd;
|
||||
use crate::cmd::utilities::CmdError;
|
||||
use crate::container_registry::{
|
||||
ContainerRegistry, ContainerRegistryError, Kind, PushError, PushResult,
|
||||
};
|
||||
use crate::container_registry::{ContainerRegistry, EngineError, Kind, PushResult};
|
||||
use crate::error::EngineErrorCause;
|
||||
use crate::models::{Context, Listener, Listeners, ProgressListener};
|
||||
|
||||
pub struct DockerHub {
|
||||
@@ -47,7 +45,7 @@ impl ContainerRegistry for DockerHub {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
// check the version of docker and print it as info
|
||||
let mut output_from_cmd = String::new();
|
||||
cmd::utilities::exec_with_output(
|
||||
@@ -70,19 +68,19 @@ impl ContainerRegistry for DockerHub {
|
||||
self.listeners.push(listener);
|
||||
}
|
||||
|
||||
fn on_create(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_create(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_error(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_create_error(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_delete(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_delete(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_delete_error(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_delete_error(&self) -> Result<(), EngineError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -104,20 +102,13 @@ impl ContainerRegistry for DockerHub {
|
||||
],
|
||||
envs.clone(),
|
||||
) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(exit_status) => {
|
||||
error!("Cannot login into dockerhub");
|
||||
return false;
|
||||
}
|
||||
CmdError::Io(err) => {
|
||||
error!("IO error on dockerhub login: {}", err);
|
||||
return false;
|
||||
}
|
||||
CmdError::Unexpected(err) => {
|
||||
error!("Unexpected error on dockerhub login: {}", err);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
if let Some(message) = err.message {
|
||||
error!("{}", message);
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@@ -130,23 +121,24 @@ impl ContainerRegistry for DockerHub {
|
||||
let mut exist_stdoud: bool = false;
|
||||
let mut exist_stderr: bool = true;
|
||||
|
||||
// TODO Change this by using curl lib
|
||||
cmd::utilities::exec_with_envs_and_output(
|
||||
"curl",
|
||||
vec!["--silent", "-f", "-lSL", &curl_path],
|
||||
envs.clone(),
|
||||
|r_out| match r_out {
|
||||
Ok(s) => exist_stdoud = true,
|
||||
Ok(_) => exist_stdoud = true,
|
||||
Err(e) => error!("Error while getting stdout from curl {}", e),
|
||||
},
|
||||
|r_err| match r_err {
|
||||
Ok(s) => exist_stderr = true,
|
||||
Ok(_) => exist_stderr = true,
|
||||
Err(e) => error!("Error while getting stderr from curl {}", e),
|
||||
},
|
||||
);
|
||||
exist_stdoud
|
||||
}
|
||||
|
||||
fn push(&self, image: &Image, force_push: bool) -> Result<PushResult, PushError> {
|
||||
fn push(&self, image: &Image, force_push: bool) -> Result<PushResult, EngineError> {
|
||||
let envs = match self.context.docker_tcp_socket() {
|
||||
Some(tcp_socket) => vec![("DOCKER_HOST", tcp_socket.as_str())],
|
||||
None => vec![],
|
||||
@@ -163,11 +155,14 @@ impl ContainerRegistry for DockerHub {
|
||||
],
|
||||
envs.clone(),
|
||||
) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(exit_status) => return Err(PushError::CredentialsError),
|
||||
CmdError::Io(err) => return Err(PushError::IoError(err)),
|
||||
CmdError::Unexpected(err) => return Err(PushError::Unknown(err)),
|
||||
},
|
||||
Err(_) => {
|
||||
return 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()))
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@@ -181,20 +176,30 @@ impl ContainerRegistry for DockerHub {
|
||||
],
|
||||
envs.clone(),
|
||||
) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(exit_status) => return Err(PushError::ImageTagFailed),
|
||||
CmdError::Io(err) => return Err(PushError::IoError(err)),
|
||||
CmdError::Unexpected(err) => return Err(PushError::Unknown(err)),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to tag image ({}) {:?}",
|
||||
image.name_with_tag(),
|
||||
image,
|
||||
),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
match cmd::utilities::exec_with_envs("docker", vec!["push", dest.as_str()], envs) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(exit_status) => return Err(PushError::ImagePushFailed),
|
||||
CmdError::Io(err) => return Err(PushError::IoError(err)),
|
||||
CmdError::Unexpected(err) => return Err(PushError::Unknown(err)),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to push image {:?} into DockerHub {}",
|
||||
image,
|
||||
self.name_with_id(),
|
||||
),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@@ -204,7 +209,7 @@ impl ContainerRegistry for DockerHub {
|
||||
Ok(PushResult { image })
|
||||
}
|
||||
|
||||
fn push_error(&self, _image: &Image) -> Result<PushResult, PushError> {
|
||||
fn push_error(&self, _image: &Image) -> Result<PushResult, EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,8 @@ use digitalocean::DigitalOcean;
|
||||
|
||||
use crate::build_platform::Image;
|
||||
use crate::cmd;
|
||||
use crate::cmd::utilities::CmdError;
|
||||
use crate::container_registry::{
|
||||
ContainerRegistry, ContainerRegistryError, Kind, PushError, PushResult,
|
||||
};
|
||||
use crate::container_registry::{ContainerRegistry, EngineError, Kind, PushResult};
|
||||
use crate::error::{EngineErrorCause, EngineErrorScope};
|
||||
use crate::models::{Context, Listener, ProgressListener};
|
||||
|
||||
// TODO : use --output json
|
||||
@@ -33,7 +31,7 @@ impl DOCR {
|
||||
DigitalOcean::new(self.api_key.as_str()).unwrap()
|
||||
}
|
||||
|
||||
pub fn create_repository(&self, _image: &Image) -> Result<(), ContainerRegistryError> {
|
||||
pub fn create_repository(&self, _image: &Image) -> Result<(), EngineError> {
|
||||
match cmd::utilities::exec(
|
||||
"doctl",
|
||||
vec![
|
||||
@@ -44,35 +42,47 @@ impl DOCR {
|
||||
self.api_key.as_str(),
|
||||
],
|
||||
) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(_exit_status) => return Err(ContainerRegistryError::Unknown),
|
||||
CmdError::Io(err) => return Err(ContainerRegistryError::Unknown),
|
||||
CmdError::Unexpected(err) => return Err(ContainerRegistryError::Unknown),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!("failed to create DOCR {}", self.registry_name.as_str()),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn push_image(&self, dest: String, image: &Image) -> Result<PushResult, PushError> {
|
||||
pub fn push_image(&self, dest: String, image: &Image) -> Result<PushResult, EngineError> {
|
||||
match cmd::utilities::exec(
|
||||
"docker",
|
||||
vec!["tag", image.name_with_tag().as_str(), dest.as_str()],
|
||||
) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(_exit_status) => return Err(PushError::ImageTagFailed),
|
||||
CmdError::Io(err) => return Err(PushError::IoError(err)),
|
||||
CmdError::Unexpected(err) => return Err(PushError::Unknown(err)),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to tag image ({}) {:?}",
|
||||
image.name_with_tag(),
|
||||
image,
|
||||
),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
match cmd::utilities::exec("docker", vec!["push", dest.as_str()]) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(_exit_status) => return Err(PushError::ImagePushFailed),
|
||||
CmdError::Io(err) => return Err(PushError::IoError(err)),
|
||||
CmdError::Unexpected(err) => return Err(PushError::Unknown(err)),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to push image {:?} into DOCR {}",
|
||||
image,
|
||||
self.name_with_id(),
|
||||
),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@@ -82,12 +92,12 @@ impl DOCR {
|
||||
Ok(PushResult { image })
|
||||
}
|
||||
|
||||
fn get_or_create_repository(&self, _image: &Image) -> Result<(), ContainerRegistryError> {
|
||||
fn get_or_create_repository(&self, _image: &Image) -> Result<(), EngineError> {
|
||||
// TODO check if repository really exist
|
||||
self.create_repository(&_image)
|
||||
}
|
||||
|
||||
fn delete_repository(&self, _image: &Image) -> Result<(), ContainerRegistryError> {
|
||||
fn delete_repository(&self, _image: &Image) -> Result<(), EngineError> {
|
||||
match cmd::utilities::exec(
|
||||
"doctl",
|
||||
vec![
|
||||
@@ -99,11 +109,16 @@ impl DOCR {
|
||||
self.api_key.as_str(),
|
||||
],
|
||||
) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(exit_status) => return Err(ContainerRegistryError::Unknown),
|
||||
CmdError::Io(err) => return Err(ContainerRegistryError::Unknown),
|
||||
CmdError::Unexpected(err) => return Err(ContainerRegistryError::Unknown),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to delete DOCR repository {} from {}",
|
||||
self.registry_name.as_str(),
|
||||
self.name_with_id(),
|
||||
),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Ok(())
|
||||
@@ -127,7 +142,7 @@ impl ContainerRegistry for DOCR {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -135,19 +150,19 @@ impl ContainerRegistry for DOCR {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_create(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_create(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_create_error(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_create_error(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_delete(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_delete(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_delete_error(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_delete_error(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -156,10 +171,12 @@ 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, PushError> {
|
||||
fn push(&self, image: &Image, _force_push: bool) -> Result<PushResult, EngineError> {
|
||||
let image = image.clone();
|
||||
//TODO instead use get_or_create_repository
|
||||
self.create_repository(&image);
|
||||
// TODO 1/ instead use get_or_create_repository
|
||||
// TODO 2/ does an error is returned if the repository already exist or not?
|
||||
self.create_repository(&image)?;
|
||||
|
||||
match cmd::utilities::exec(
|
||||
"doctl",
|
||||
vec![
|
||||
@@ -170,19 +187,23 @@ impl ContainerRegistry for DOCR {
|
||||
self.api_key.as_str(),
|
||||
],
|
||||
) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(_exit_status) => return Err(PushError::CredentialsError),
|
||||
CmdError::Io(err) => return Err(PushError::IoError(err)),
|
||||
CmdError::Unexpected(err) => return Err(PushError::Unknown(err)),
|
||||
},
|
||||
Err(_) => {
|
||||
return 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()))
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
//TODO check force or not
|
||||
let dest = format!("{}:{}", self.registry_name.as_str(), image.tag.as_str());
|
||||
self.push_image(dest, &image)
|
||||
}
|
||||
|
||||
fn push_error(&self, _image: &Image) -> Result<PushResult, PushError> {
|
||||
fn push_error(&self, _image: &Image) -> Result<PushResult, EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,8 @@ use rusoto_sts::{GetCallerIdentityRequest, Sts, StsClient};
|
||||
|
||||
use crate::build_platform::Image;
|
||||
use crate::cmd;
|
||||
use crate::cmd::utilities::CmdError;
|
||||
use crate::container_registry::{
|
||||
ContainerRegistry, ContainerRegistryError, Kind, PushError, PushResult,
|
||||
};
|
||||
use crate::container_registry::{ContainerRegistry, Kind, PushResult};
|
||||
use crate::error::{EngineError, EngineErrorCause};
|
||||
use crate::models::{
|
||||
Context, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressListener,
|
||||
ProgressScope,
|
||||
@@ -112,7 +110,7 @@ impl ECR {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_image(&self, dest: String, image: &Image) -> Result<PushResult, PushError> {
|
||||
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
|
||||
|
||||
@@ -121,11 +119,16 @@ impl ECR {
|
||||
vec!["tag", image.name_with_tag().as_str(), dest.as_str()],
|
||||
self.docker_envs(),
|
||||
) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(_exit_status) => return Err(PushError::ImageTagFailed),
|
||||
CmdError::Io(err) => return Err(PushError::IoError(err)),
|
||||
CmdError::Unexpected(err) => return Err(PushError::Unknown(err)),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to tag image ({}) {:?}",
|
||||
image.name_with_tag(),
|
||||
image,
|
||||
),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@@ -135,11 +138,16 @@ impl ECR {
|
||||
vec!["push", dest.as_str()],
|
||||
self.docker_envs(),
|
||||
) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(_exit_status) => return Err(PushError::ImagePushFailed),
|
||||
CmdError::Io(err) => return Err(PushError::IoError(err)),
|
||||
CmdError::Unexpected(err) => return Err(PushError::Unknown(err)),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to push image {:?} into ECR {}",
|
||||
image,
|
||||
self.name_with_id(),
|
||||
),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@@ -149,7 +157,7 @@ impl ECR {
|
||||
Ok(PushResult { image })
|
||||
}
|
||||
|
||||
fn create_repository(&self, image: &Image) -> Result<Repository, ContainerRegistryError> {
|
||||
fn create_repository(&self, image: &Image) -> Result<Repository, EngineError> {
|
||||
info!("ECR create repository {}", image.name.as_str());
|
||||
let mut crr = CreateRepositoryRequest::default();
|
||||
crr.repository_name = image.name.clone();
|
||||
@@ -158,7 +166,16 @@ impl ECR {
|
||||
match r {
|
||||
Err(err) => match err {
|
||||
RusotoError::Service(ref err) => info!("{:?}", err),
|
||||
_ => return Err(ContainerRegistryError::from(err)),
|
||||
_ => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"can't create ECR repository {} for {}",
|
||||
image.name.as_str(),
|
||||
self.name_with_id()
|
||||
),
|
||||
));
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
@@ -191,15 +208,19 @@ impl ECR {
|
||||
let r = async_run(self.ecr_client().put_lifecycle_policy(plp));
|
||||
|
||||
match r {
|
||||
Err(err) => Err(ContainerRegistryError::from(err)),
|
||||
Err(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()
|
||||
),
|
||||
)),
|
||||
_ => Ok(self.get_repository(&image).unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_create_repository(
|
||||
&self,
|
||||
image: &Image,
|
||||
) -> Result<Repository, ContainerRegistryError> {
|
||||
fn get_or_create_repository(&self, image: &Image) -> Result<Repository, EngineError> {
|
||||
// check if the repository already exists
|
||||
let repository = self.get_repository(&image);
|
||||
if repository.is_some() {
|
||||
@@ -228,13 +249,19 @@ impl ContainerRegistry for ECR {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
let client = StsClient::new_with_client(self.client(), Region::default());
|
||||
let s = async_run(client.get_caller_identity(GetCallerIdentityRequest::default()));
|
||||
|
||||
match s {
|
||||
Ok(_x) => Ok(()),
|
||||
Err(err) => Err(ContainerRegistryError::from(err)),
|
||||
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()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,28 +269,28 @@ impl ContainerRegistry for ECR {
|
||||
self.listeners.push(listener);
|
||||
}
|
||||
|
||||
fn on_create(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_create(&self) -> Result<(), EngineError> {
|
||||
info!("ECR.on_create() called");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_error(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_create_error(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_delete(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_delete(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_delete_error(&self) -> Result<(), ContainerRegistryError> {
|
||||
fn on_delete_error(&self) -> Result<(), EngineError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn does_image_exists(&self, image: &Image) -> bool {
|
||||
self.get_repository(&image).is_some()
|
||||
self.get_repository(image).is_some()
|
||||
}
|
||||
|
||||
fn push(&self, image: &Image, force_push: bool) -> Result<PushResult, PushError> {
|
||||
fn push(&self, image: &Image, force_push: bool) -> Result<PushResult, EngineError> {
|
||||
let r = async_run(
|
||||
self.ecr_client()
|
||||
.get_authorization_token(GetAuthorizationTokenRequest::default()),
|
||||
@@ -286,18 +313,43 @@ impl ContainerRegistry for ECR {
|
||||
ad.clone().proxy_endpoint.unwrap(),
|
||||
)
|
||||
}
|
||||
None => return Err(PushError::RepositoryInitFailure),
|
||||
None => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to retrieve credentials and endpoint URL from ECR {}",
|
||||
self.name_with_id(),
|
||||
),
|
||||
));
|
||||
}
|
||||
},
|
||||
_ => return Err(PushError::RepositoryInitFailure),
|
||||
_ => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to retrieve credentials and endpoint URL from ECR {}",
|
||||
self.name_with_id(),
|
||||
),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let repository = match if force_push {
|
||||
self.create_repository(&image)
|
||||
self.create_repository(image)
|
||||
} else {
|
||||
self.get_or_create_repository(&image)
|
||||
self.get_or_create_repository(image)
|
||||
} {
|
||||
Ok(r) => r,
|
||||
_ => return Err(PushError::RepositoryInitFailure),
|
||||
_ => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to create ECR repository for {} with image {:?}",
|
||||
self.name_with_id(),
|
||||
image,
|
||||
),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
match cmd::utilities::exec_with_envs(
|
||||
@@ -312,11 +364,12 @@ impl ContainerRegistry for ECR {
|
||||
],
|
||||
self.docker_envs(),
|
||||
) {
|
||||
Err(err) => match err {
|
||||
CmdError::Exec(_exit_status) => return Err(PushError::CredentialsError),
|
||||
CmdError::Io(err) => return Err(PushError::IoError(err)),
|
||||
CmdError::Unexpected(err) => return Err(PushError::Unknown(err)),
|
||||
},
|
||||
Err(_) => 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()))
|
||||
),
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@@ -373,7 +426,7 @@ impl ContainerRegistry for ECR {
|
||||
self.push_image(dest, image)
|
||||
}
|
||||
|
||||
fn push_error(&self, image: &Image) -> Result<PushResult, PushError> {
|
||||
fn push_error(&self, image: &Image) -> Result<PushResult, EngineError> {
|
||||
// TODO change this
|
||||
Ok(PushResult {
|
||||
image: image.clone(),
|
||||
|
||||
@@ -5,6 +5,7 @@ use rusoto_core::RusotoError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::build_platform::Image;
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
|
||||
use crate::models::{Context, Listener, ProgressListener};
|
||||
|
||||
pub mod docker_hub;
|
||||
@@ -16,61 +17,38 @@ pub trait ContainerRegistry {
|
||||
fn kind(&self) -> Kind;
|
||||
fn id(&self) -> &str;
|
||||
fn name(&self) -> &str;
|
||||
fn is_valid(&self) -> Result<(), ContainerRegistryError>;
|
||||
fn name_with_id(&self) -> String {
|
||||
format!("{} ({})", self.name(), self.id())
|
||||
}
|
||||
fn is_valid(&self) -> Result<(), EngineError>;
|
||||
fn add_listener(&mut self, listener: Listener);
|
||||
fn on_create(&self) -> Result<(), ContainerRegistryError>;
|
||||
fn on_create_error(&self) -> Result<(), ContainerRegistryError>;
|
||||
fn on_delete(&self) -> Result<(), ContainerRegistryError>;
|
||||
fn on_delete_error(&self) -> Result<(), ContainerRegistryError>;
|
||||
fn on_create(&self) -> Result<(), EngineError>;
|
||||
fn on_create_error(&self) -> Result<(), EngineError>;
|
||||
fn on_delete(&self) -> Result<(), EngineError>;
|
||||
fn on_delete_error(&self) -> Result<(), EngineError>;
|
||||
fn does_image_exists(&self, image: &Image) -> bool;
|
||||
fn push(&self, image: &Image, force_push: bool) -> Result<PushResult, PushError>;
|
||||
fn push_error(&self, image: &Image) -> Result<PushResult, PushError>;
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PushResult {
|
||||
pub image: Image,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PushError {
|
||||
RepositoryInitFailure,
|
||||
CredentialsError,
|
||||
IoError(std::io::Error),
|
||||
ImageTagFailed,
|
||||
ImagePushFailed,
|
||||
ImageAlreadyExists,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub enum Kind {
|
||||
DockerHub,
|
||||
ECR,
|
||||
DOCR,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum ContainerRegistryError {
|
||||
Credentials,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl<E> From<RusotoError<E>> for ContainerRegistryError {
|
||||
fn from(error: RusotoError<E>) -> Self {
|
||||
match error {
|
||||
RusotoError::Credentials(_) => ContainerRegistryError::Credentials,
|
||||
RusotoError::Service(_) => ContainerRegistryError::Unknown,
|
||||
RusotoError::HttpDispatch(_) => ContainerRegistryError::Unknown,
|
||||
RusotoError::Validation(_) => ContainerRegistryError::Unknown,
|
||||
RusotoError::ParseError(_) => ContainerRegistryError::Unknown,
|
||||
RusotoError::Unknown(e) => {
|
||||
if e.status == 403 {
|
||||
ContainerRegistryError::Credentials
|
||||
} else {
|
||||
ContainerRegistryError::Unknown
|
||||
}
|
||||
}
|
||||
RusotoError::Blocking => ContainerRegistryError::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::dns_provider::{DnsProvider, DnsProviderError, Kind};
|
||||
use crate::dns_provider::{DnsProvider, Kind};
|
||||
use crate::error::{EngineError, EngineErrorCause};
|
||||
use crate::models::Context;
|
||||
|
||||
pub struct Cloudflare {
|
||||
@@ -65,9 +66,15 @@ impl DnsProvider for Cloudflare {
|
||||
vec![Ipv4Addr::new(1, 1, 1, 1), Ipv4Addr::new(1, 0, 0, 1)]
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> Result<(), DnsProviderError> {
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
if self.cloudflare_api_token.is_empty() || self.cloudflare_email.is_empty() {
|
||||
Err(DnsProviderError::Credentials)
|
||||
Err(self.engine_error(
|
||||
EngineErrorCause::User(
|
||||
"Your Cloudflare account seems to be no longer valid (bad Credentials). \
|
||||
Please contact your Organization administrator to fix or change the Credentials.",
|
||||
),
|
||||
format!("bad Cloudflare credentials for {}", self.name_with_id()),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::net::Ipv4Addr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
|
||||
use crate::models::Context;
|
||||
|
||||
pub mod cloudflare;
|
||||
@@ -16,15 +17,23 @@ pub trait DnsProvider {
|
||||
fn kind(&self) -> Kind;
|
||||
fn id(&self) -> &str;
|
||||
fn name(&self) -> &str;
|
||||
fn name_with_id(&self) -> String {
|
||||
format!("{} ({})", self.name(), self.id())
|
||||
}
|
||||
fn account(&self) -> &str;
|
||||
fn token(&self) -> &str;
|
||||
fn domain(&self) -> &str;
|
||||
fn resolvers(&self) -> Vec<Ipv4Addr>;
|
||||
fn is_valid(&self) -> Result<(), DnsProviderError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum DnsProviderError {
|
||||
Credentials,
|
||||
Unknown,
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::borrow::Borrow;
|
||||
use crate::build_platform::BuildPlatform;
|
||||
use crate::cloud_provider::CloudProvider;
|
||||
use crate::container_registry::ContainerRegistry;
|
||||
use crate::dns_provider::{DnsProvider, DnsProviderError};
|
||||
use crate::error::ConfigurationError;
|
||||
use crate::dns_provider::DnsProvider;
|
||||
use crate::error::EngineError;
|
||||
use crate::models::Context;
|
||||
use crate::session::Session;
|
||||
|
||||
@@ -50,44 +50,22 @@ impl<'a> Engine {
|
||||
pub fn cloud_provider(&self) -> &dyn CloudProvider {
|
||||
self.cloud_provider.borrow()
|
||||
}
|
||||
|
||||
pub fn dns_provider(&self) -> &dyn DnsProvider {
|
||||
self.dns_provider.borrow()
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> Result<(), ConfigurationError> {
|
||||
match self.build_platform.is_valid() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
return Err(ConfigurationError::BuildPlatform(err));
|
||||
}
|
||||
}
|
||||
|
||||
match self.container_registry.is_valid() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
return Err(ConfigurationError::ContainerRegistry(err));
|
||||
}
|
||||
}
|
||||
|
||||
match self.cloud_provider.is_valid() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
return Err(ConfigurationError::CloudProvider(err));
|
||||
}
|
||||
}
|
||||
|
||||
match self.dns_provider.is_valid() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
return Err(ConfigurationError::DnsProvider(err));
|
||||
}
|
||||
}
|
||||
pub fn is_valid(&self) -> Result<(), EngineError> {
|
||||
self.build_platform.is_valid()?;
|
||||
self.container_registry.is_valid()?;
|
||||
self.cloud_provider.is_valid()?;
|
||||
self.dns_provider.is_valid()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// check and init the connection to all the services
|
||||
pub fn session(&'a self) -> Result<Session<'a>, ConfigurationError> {
|
||||
pub fn session(&'a self) -> Result<Session<'a>, EngineError> {
|
||||
match self.is_valid() {
|
||||
Ok(_) => Ok(Session::<'a> { engine: self }),
|
||||
Err(err) => Err(err),
|
||||
|
||||
123
src/error.rs
123
src/error.rs
@@ -1,12 +1,117 @@
|
||||
use crate::build_platform::error::BuildPlatformError;
|
||||
use crate::cloud_provider::CloudProviderError;
|
||||
use crate::container_registry::ContainerRegistryError;
|
||||
use crate::dns_provider::DnsProviderError;
|
||||
use std::error::Error;
|
||||
use std::process::ExitStatus;
|
||||
|
||||
use rusoto_core::RusotoError;
|
||||
|
||||
pub type Type = String;
|
||||
pub type Id = String;
|
||||
pub type Name = String;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ConfigurationError {
|
||||
BuildPlatform(BuildPlatformError),
|
||||
ContainerRegistry(ContainerRegistryError),
|
||||
CloudProvider(CloudProviderError),
|
||||
DnsProvider(DnsProviderError),
|
||||
pub struct EngineError {
|
||||
pub cause: EngineErrorCause,
|
||||
pub scope: EngineErrorScope,
|
||||
pub execution_id: String,
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
impl EngineError {
|
||||
pub fn new<T, S>(
|
||||
cause: EngineErrorCause,
|
||||
scope: EngineErrorScope,
|
||||
execution_id: T,
|
||||
message: Option<S>,
|
||||
) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
S: Into<String>,
|
||||
{
|
||||
EngineError {
|
||||
cause,
|
||||
scope,
|
||||
execution_id: execution_id.into(),
|
||||
message: match message {
|
||||
Some(message) => Some(message.into()),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EngineErrorScope {
|
||||
Engine,
|
||||
BuildPlatform(Id, Name),
|
||||
ContainerRegistry(Id, Name),
|
||||
CloudProvider(Id, Name),
|
||||
Kubernetes(Id, Name),
|
||||
DnsProvider(Id, Name),
|
||||
Environment(Id, Name),
|
||||
Database(Id, Type, Name),
|
||||
Application(Id, Name),
|
||||
Router(Id, Name),
|
||||
ExternalService(Id, Name),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EngineErrorCause {
|
||||
Internal,
|
||||
User(&'static str),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SimpleError {
|
||||
pub kind: SimpleErrorKind,
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SimpleErrorKind {
|
||||
Command(ExitStatus),
|
||||
Other,
|
||||
}
|
||||
|
||||
impl SimpleError {
|
||||
pub fn new<T: Into<String>>(kind: SimpleErrorKind, message: Option<T>) -> Self {
|
||||
SimpleError {
|
||||
kind,
|
||||
message: match message {
|
||||
Some(message) => Some(message.into()),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for SimpleError {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
SimpleError::new(SimpleErrorKind::Other, Some(err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_simple_error_to_engine_error<X, T: Into<String>>(
|
||||
scope: EngineErrorScope,
|
||||
execution_id: T,
|
||||
input: Result<X, SimpleError>,
|
||||
) -> Result<X, EngineError> {
|
||||
match input {
|
||||
Err(simple_error) => {
|
||||
let message = match simple_error.kind {
|
||||
SimpleErrorKind::Command(exit_status) => format!(
|
||||
"{} (exit status: {})",
|
||||
simple_error.message.unwrap_or("<no message>".into()),
|
||||
exit_status
|
||||
),
|
||||
SimpleErrorKind::Other => simple_error.message.unwrap_or("<no message>".into()),
|
||||
};
|
||||
|
||||
Err(EngineError::new(
|
||||
EngineErrorCause::Internal,
|
||||
scope,
|
||||
execution_id,
|
||||
Some(message),
|
||||
))
|
||||
}
|
||||
Ok(x) => Ok(x),
|
||||
}
|
||||
}
|
||||
|
||||
92
src/s3.rs
92
src/s3.rs
@@ -1,3 +1,11 @@
|
||||
use std::fmt::Display;
|
||||
use std::fs::{read_to_string, File};
|
||||
use std::io::{Error, ErrorKind, Read, Write};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::{fs, io};
|
||||
|
||||
use retry::delay::Fibonacci;
|
||||
use retry::OperationResult;
|
||||
use rusoto_core::{Client, HttpClient, Region, RusotoError};
|
||||
@@ -7,23 +15,20 @@ use rusoto_s3::{
|
||||
GetObjectRequest, ListObjectsV2Output, ListObjectsV2Request, PutBucketVersioningRequest,
|
||||
S3Client, VersioningConfiguration, S3,
|
||||
};
|
||||
use std::fmt::Display;
|
||||
use std::fs::{read_to_string, File};
|
||||
use std::io::{Error, ErrorKind, Read, Write};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
|
||||
use crate::cmd::utilities::exec_with_envs;
|
||||
use crate::error::{SimpleError, SimpleErrorKind};
|
||||
use crate::runtime::async_run;
|
||||
|
||||
pub const AWS_REGION_FOR_S3_US: &str = "ap-south-1";
|
||||
|
||||
use crate::cmd::utilities::{exec_with_envs, CmdError};
|
||||
use crate::runtime::async_run;
|
||||
use std::str::FromStr;
|
||||
pub type FileContent = String;
|
||||
|
||||
pub fn create_bucket(
|
||||
access_key_id: &str,
|
||||
secret_access_key: &str,
|
||||
bucket_name: &str,
|
||||
) -> Result<(), CmdError> {
|
||||
) -> Result<(), SimpleError> {
|
||||
exec_with_envs(
|
||||
"aws",
|
||||
vec!["s3api", "create-bucket", "--bucket", &bucket_name],
|
||||
@@ -34,15 +39,13 @@ pub fn create_bucket(
|
||||
)
|
||||
}
|
||||
|
||||
pub type FileContent = String;
|
||||
|
||||
pub fn get_object(
|
||||
access_key_id: &str,
|
||||
secret_access_key: &str,
|
||||
region: &Region,
|
||||
bucket_name: &str,
|
||||
object_key: &str,
|
||||
) -> Result<FileContent, Error> {
|
||||
) -> Result<FileContent, SimpleError> {
|
||||
let credentials = StaticProvider::new(
|
||||
access_key_id.to_string(),
|
||||
secret_access_key.to_string(),
|
||||
@@ -59,12 +62,12 @@ pub fn get_object(
|
||||
let get_object_output = s3_client.get_object(or);
|
||||
let r = async_run(get_object_output);
|
||||
|
||||
let _err = Error::new(
|
||||
ErrorKind::Other,
|
||||
format!(
|
||||
let _err = SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some(format!(
|
||||
"something goes wrong while getting object {} in the S3 bucket {}",
|
||||
object_key, bucket_name
|
||||
),
|
||||
)),
|
||||
);
|
||||
|
||||
match r {
|
||||
@@ -90,9 +93,9 @@ pub fn get_object(
|
||||
RusotoError::Service(s) => match s {
|
||||
GetObjectError::NoSuchKey(x) => {
|
||||
info!("no such key '{}': {}", object_key, x.as_str());
|
||||
Err(Error::new(
|
||||
ErrorKind::NotFound,
|
||||
format!("no such key '{}': {}", object_key, x.as_str()),
|
||||
Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some(format!("no such key '{}': {}", object_key, x.as_str())),
|
||||
))
|
||||
}
|
||||
},
|
||||
@@ -107,7 +110,10 @@ pub fn get_object(
|
||||
match r_from_aws_cli {
|
||||
Ok(..) => Ok(r_from_aws_cli.unwrap()),
|
||||
Err(err) => {
|
||||
error!("{}", err);
|
||||
if let Some(message) = err.message {
|
||||
error!("{}", message);
|
||||
}
|
||||
|
||||
Err(_err)
|
||||
}
|
||||
}
|
||||
@@ -125,21 +131,19 @@ fn get_object_via_aws_cli(
|
||||
secret_access_key: &str,
|
||||
bucket_name: &str,
|
||||
object_key: &str,
|
||||
) -> Result<FileContent, Error> {
|
||||
) -> Result<FileContent, SimpleError> {
|
||||
let s3_url = format!("s3://{}/{}", bucket_name, object_key);
|
||||
let local_path = format!("/tmp/{}", object_key);
|
||||
let r = exec_with_envs(
|
||||
|
||||
exec_with_envs(
|
||||
"aws",
|
||||
vec!["s3", "cp", &s3_url, &local_path],
|
||||
vec![
|
||||
("AWS_ACCESS_KEY_ID", &access_key_id),
|
||||
("AWS_SECRET_ACCESS_KEY", &secret_access_key),
|
||||
],
|
||||
);
|
||||
match r {
|
||||
Err(e) => return Err(Error::new(ErrorKind::Other, e)),
|
||||
_ => {}
|
||||
};
|
||||
)?;
|
||||
|
||||
let s = read_to_string(&local_path)?;
|
||||
Ok(s)
|
||||
}
|
||||
@@ -151,7 +155,7 @@ pub fn get_kubernetes_config_file<P>(
|
||||
kubernetes_config_bucket_name: &str,
|
||||
kubernetes_config_object_key: &str,
|
||||
file_path: P,
|
||||
) -> Result<File, Error>
|
||||
) -> Result<File, SimpleError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
@@ -183,9 +187,9 @@ where
|
||||
let file_content = match file_content_result {
|
||||
Ok(file_content) => file_content,
|
||||
Err(_) => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"file content is empty (retry failed multiple times) - which is not the expected content - what's wrong?",
|
||||
return Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some("file content is empty (retry failed multiple times) - which is not the expected content - what's wrong?"),
|
||||
));
|
||||
}
|
||||
};
|
||||
@@ -204,7 +208,7 @@ pub fn list_objects_in(
|
||||
access_key_id: &str,
|
||||
secret_access_key: &str,
|
||||
bucket_name: &str,
|
||||
) -> Result<ListObjectsV2Output, Error> {
|
||||
) -> Result<ListObjectsV2Output, SimpleError> {
|
||||
let credentials = StaticProvider::new(
|
||||
access_key_id.to_string(),
|
||||
secret_access_key.to_string(),
|
||||
@@ -213,13 +217,19 @@ pub fn list_objects_in(
|
||||
);
|
||||
let client = Client::new_with(credentials, HttpClient::new().unwrap());
|
||||
let s3_client = S3Client::new_with_client(client, get_default_region_for_us());
|
||||
|
||||
let mut list_request = ListObjectsV2Request::default();
|
||||
list_request.bucket = bucket_name.to_string();
|
||||
|
||||
let lis_object = s3_client.list_objects_v2(list_request);
|
||||
let objects_in = async_run(lis_object);
|
||||
|
||||
match objects_in {
|
||||
Ok(objects) => Ok(objects),
|
||||
Err(err) => Err(Error::new(ErrorKind::Other, err)),
|
||||
Err(err) => Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some(format!("error listing objects from s3 {:?}", err)),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +238,7 @@ pub fn delete_bucket(
|
||||
access_key_id: &str,
|
||||
secret_access_key: &str,
|
||||
bucket_name: &str,
|
||||
) -> Result<(), CmdError> {
|
||||
) -> Result<(), SimpleError> {
|
||||
info!("Deleting S3 Bucket {}", bucket_name.clone());
|
||||
match exec_with_envs(
|
||||
"aws",
|
||||
@@ -249,7 +259,10 @@ pub fn delete_bucket(
|
||||
return Ok(o);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("while deleting bucket {}", e);
|
||||
error!(
|
||||
"while deleting bucket {}",
|
||||
e.message.as_ref().unwrap_or(&"".into())
|
||||
);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
@@ -265,7 +278,7 @@ pub fn push_object(
|
||||
bucket_name: &str,
|
||||
object_key: &str,
|
||||
local_file_path: &str,
|
||||
) -> Result<(), CmdError> {
|
||||
) -> Result<(), SimpleError> {
|
||||
info!(
|
||||
"Pushing object {} to bucket {}",
|
||||
local_file_path.clone(),
|
||||
@@ -289,7 +302,10 @@ pub fn push_object(
|
||||
return Ok(o);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("While uploading object {}", e);
|
||||
error!(
|
||||
"While uploading object {}",
|
||||
e.message.as_ref().unwrap_or(&"".into())
|
||||
);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::io::{Error, ErrorKind, Write};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::{SimpleError, SimpleErrorKind};
|
||||
use tera::Error as TeraError;
|
||||
use tera::{Context, Tera};
|
||||
use walkdir::WalkDir;
|
||||
@@ -13,7 +14,7 @@ pub fn generate_and_copy_all_files_into_dir<S, P>(
|
||||
from_dir: S,
|
||||
to_dir: P,
|
||||
context: &Context,
|
||||
) -> Result<(), Error>
|
||||
) -> Result<(), SimpleError>
|
||||
where
|
||||
S: AsRef<Path> + Copy,
|
||||
P: AsRef<Path> + Copy,
|
||||
@@ -49,7 +50,7 @@ where
|
||||
};
|
||||
|
||||
error!("{}", error_msg.as_str());
|
||||
return Err(Error::new(ErrorKind::InvalidData, error_msg));
|
||||
return Err(SimpleError::new(SimpleErrorKind::Other, Some(error_msg)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -61,12 +62,15 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn copy_non_template_files<S, P>(from: S, to: P) -> Result<(), Error>
|
||||
pub fn copy_non_template_files<S, P>(from: S, to: P) -> Result<(), SimpleError>
|
||||
where
|
||||
S: AsRef<Path>,
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
crate::fs::copy_files(from.as_ref(), to.as_ref(), true)
|
||||
match crate::fs::copy_files(from.as_ref(), to.as_ref(), true) {
|
||||
Err(err) => Err(SimpleError::from(err)),
|
||||
Ok(x) => Ok(x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_j2_template_files<P>(
|
||||
@@ -117,7 +121,7 @@ where
|
||||
pub fn write_rendered_templates(
|
||||
rendered_templates: &[RenderedTemplate],
|
||||
into: &Path,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), SimpleError> {
|
||||
for rt in rendered_templates {
|
||||
let dest = format!("{}/{}", into.to_str().unwrap(), rt.path_and_file_name());
|
||||
|
||||
|
||||
@@ -3,13 +3,11 @@ use std::thread;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::build_platform::BuildError;
|
||||
use crate::cloud_provider::kubernetes::{Kubernetes, KubernetesError};
|
||||
use crate::cloud_provider::service::ServiceError;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::service::{Application, Service};
|
||||
use crate::cloud_provider::DeployError;
|
||||
use crate::container_registry::{PushError, PushResult};
|
||||
use crate::container_registry::PushResult;
|
||||
use crate::engine::Engine;
|
||||
use crate::error::EngineError;
|
||||
use crate::models::{
|
||||
Action, Environment, EnvironmentAction, EnvironmentError, ListenersHelper, ProgressInfo,
|
||||
ProgressLevel, ProgressScope,
|
||||
@@ -30,10 +28,7 @@ impl<'a> Transaction<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_kubernetes(
|
||||
&mut self,
|
||||
kubernetes: &'a dyn Kubernetes,
|
||||
) -> Result<(), KubernetesError> {
|
||||
pub fn create_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), EngineError> {
|
||||
match kubernetes.is_valid() {
|
||||
Ok(_) => {
|
||||
self.steps.push(Step::CreateKubernetes(kubernetes));
|
||||
@@ -43,10 +38,7 @@ impl<'a> Transaction<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_kubernetes(
|
||||
&mut self,
|
||||
kubernetes: &'a dyn Kubernetes,
|
||||
) -> Result<(), KubernetesError> {
|
||||
pub fn delete_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), EngineError> {
|
||||
match kubernetes.is_valid() {
|
||||
Ok(_) => {
|
||||
self.steps.push(Step::DeleteKubernetes(kubernetes));
|
||||
@@ -143,7 +135,7 @@ impl<'a> Transaction<'a> {
|
||||
&self,
|
||||
environment: &Environment,
|
||||
option: &DeploymentOption,
|
||||
) -> Result<Vec<Box<dyn Application>>, BuildError> {
|
||||
) -> Result<Vec<Box<dyn Application>>, EngineError> {
|
||||
let external_services_to_build = environment
|
||||
.external_services
|
||||
.iter()
|
||||
@@ -238,7 +230,7 @@ impl<'a> Transaction<'a> {
|
||||
&self,
|
||||
applications: Vec<Box<dyn Application>>,
|
||||
option: &DeploymentOption,
|
||||
) -> Result<Vec<(Box<dyn Application>, PushResult)>, PushError> {
|
||||
) -> Result<Vec<(Box<dyn Application>, PushResult)>, EngineError> {
|
||||
let application_and_push_results: Vec<_> = applications
|
||||
.into_iter()
|
||||
.map(|mut app| {
|
||||
@@ -276,18 +268,13 @@ impl<'a> Transaction<'a> {
|
||||
environment: &crate::cloud_provider::environment::Environment,
|
||||
) -> TransactionResult {
|
||||
match environment.is_valid() {
|
||||
Err(service_error) => {
|
||||
warn!("ROLLBACK STARTED! an error occurred {:?}", service_error);
|
||||
Err(engine_error) => {
|
||||
warn!("ROLLBACK STARTED! an error occurred {:?}", engine_error);
|
||||
return match self.rollback() {
|
||||
Ok(_) => {
|
||||
TransactionResult::Rollback(CommitError::NotValidService(service_error))
|
||||
}
|
||||
Ok(_) => TransactionResult::Rollback(engine_error),
|
||||
Err(err) => {
|
||||
error!("ROLLBACK FAILED! fatal error: {:?}", err);
|
||||
TransactionResult::UnrecoverableError(
|
||||
CommitError::NotValidService(service_error),
|
||||
err,
|
||||
)
|
||||
TransactionResult::UnrecoverableError(engine_error, err)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -303,14 +290,14 @@ impl<'a> Transaction<'a> {
|
||||
Step::CreateKubernetes(kubernetes) => {
|
||||
// revert kubernetes creation
|
||||
match kubernetes.on_create_error() {
|
||||
Err(err) => return Err(RollbackError::CreateKubernetes(err)),
|
||||
Err(err) => return Err(RollbackError::CommitError(err)),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Step::DeleteKubernetes(kubernetes) => {
|
||||
// revert kubernetes deletion
|
||||
match kubernetes.on_delete_error() {
|
||||
Err(err) => return Err(RollbackError::DeleteKubernetes(err)),
|
||||
Err(err) => return Err(RollbackError::CommitError(err)),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
@@ -408,14 +395,7 @@ impl<'a> Transaction<'a> {
|
||||
|
||||
let _ = match action {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
return Err(match failover_environment.action {
|
||||
Action::Create => RollbackError::DeployEnvironment(err),
|
||||
Action::Pause => RollbackError::PauseEnvironment(err),
|
||||
Action::Delete => RollbackError::DeleteEnvironment(err),
|
||||
Action::Nothing => RollbackError::Error, // it can't happens
|
||||
});
|
||||
}
|
||||
Err(err) => return Err(RollbackError::CommitError(err)),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
@@ -433,14 +413,7 @@ impl<'a> Transaction<'a> {
|
||||
|
||||
let _ = match action {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
return Err(match te.action {
|
||||
Action::Create => RollbackError::DeployEnvironment(err),
|
||||
Action::Pause => RollbackError::PauseEnvironment(err),
|
||||
Action::Delete => RollbackError::DeleteEnvironment(err),
|
||||
Action::Nothing => RollbackError::Error, // it can't happens
|
||||
});
|
||||
}
|
||||
Err(err) => return Err(RollbackError::CommitError(err)),
|
||||
};
|
||||
|
||||
Err(RollbackError::NoFailoverEnvironment)
|
||||
@@ -463,15 +436,10 @@ impl<'a> Transaction<'a> {
|
||||
Err(err) => {
|
||||
warn!("ROLLBACK STARTED! an error occurred {:?}", err);
|
||||
match self.rollback() {
|
||||
Ok(_) => {
|
||||
TransactionResult::Rollback(CommitError::CreateKubernetes(err))
|
||||
}
|
||||
Ok(_) => TransactionResult::Rollback(err),
|
||||
Err(e) => {
|
||||
error!("ROLLBACK FAILED! fatal error: {:?}", e);
|
||||
TransactionResult::UnrecoverableError(
|
||||
CommitError::CreateKubernetes(err),
|
||||
e,
|
||||
)
|
||||
TransactionResult::UnrecoverableError(err, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -484,15 +452,10 @@ impl<'a> Transaction<'a> {
|
||||
Err(err) => {
|
||||
warn!("ROLLBACK STARTED! an error occurred {:?}", err);
|
||||
match self.rollback() {
|
||||
Ok(_) => {
|
||||
TransactionResult::Rollback(CommitError::DeleteKubernetes(err))
|
||||
}
|
||||
Ok(_) => TransactionResult::Rollback(err),
|
||||
Err(e) => {
|
||||
error!("ROLLBACK FAILED! fatal error: {:?}", e);
|
||||
TransactionResult::UnrecoverableError(
|
||||
CommitError::DeleteKubernetes(err),
|
||||
e,
|
||||
)
|
||||
TransactionResult::UnrecoverableError(err, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -514,9 +477,9 @@ impl<'a> Transaction<'a> {
|
||||
|
||||
Ok(applications)
|
||||
}
|
||||
Err(err) => Err(CommitError::PushImage(err)),
|
||||
Err(err) => Err(err),
|
||||
},
|
||||
Err(err) => Err(CommitError::BuildImage(err)),
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
|
||||
if apps_result.is_err() {
|
||||
@@ -542,7 +505,6 @@ impl<'a> Transaction<'a> {
|
||||
*environment_action,
|
||||
&applications_by_environment,
|
||||
|qe_env| kubernetes.deploy_environment(qe_env),
|
||||
|err| CommitError::DeployEnvironment(err),
|
||||
) {
|
||||
TransactionResult::Ok => {}
|
||||
err => return err,
|
||||
@@ -555,7 +517,6 @@ impl<'a> Transaction<'a> {
|
||||
*environment_action,
|
||||
&applications_by_environment,
|
||||
|qe_env| kubernetes.pause_environment(qe_env),
|
||||
|err| CommitError::PauseEnvironment(err),
|
||||
) {
|
||||
TransactionResult::Ok => {}
|
||||
err => return err,
|
||||
@@ -568,7 +529,6 @@ impl<'a> Transaction<'a> {
|
||||
*environment_action,
|
||||
&applications_by_environment,
|
||||
|qe_env| kubernetes.delete_environment(qe_env),
|
||||
|err| CommitError::DeleteEnvironment(err),
|
||||
) {
|
||||
TransactionResult::Ok => {}
|
||||
err => return err,
|
||||
@@ -580,17 +540,15 @@ impl<'a> Transaction<'a> {
|
||||
TransactionResult::Ok
|
||||
}
|
||||
|
||||
fn commit_environment<F, E>(
|
||||
fn commit_environment<F>(
|
||||
&self,
|
||||
kubernetes: &dyn Kubernetes,
|
||||
environment_action: &EnvironmentAction,
|
||||
applications_by_environment: &HashMap<&Environment, Vec<Box<dyn Application>>>,
|
||||
action_fn: F,
|
||||
commit_error: E,
|
||||
) -> TransactionResult
|
||||
where
|
||||
F: Fn(&crate::cloud_provider::environment::Environment) -> Result<(), KubernetesError>,
|
||||
E: Fn(KubernetesError) -> CommitError,
|
||||
F: Fn(&crate::cloud_provider::environment::Environment) -> Result<(), EngineError>,
|
||||
{
|
||||
let target_environment = match environment_action {
|
||||
EnvironmentAction::Environment(te) => te,
|
||||
@@ -668,10 +626,10 @@ impl<'a> Transaction<'a> {
|
||||
let _ = match action_fn(&qe_environment) {
|
||||
Err(err) => {
|
||||
let rollback_result = match self.rollback() {
|
||||
Ok(_) => TransactionResult::Rollback(commit_error(err)),
|
||||
Ok(_) => TransactionResult::Rollback(err),
|
||||
Err(rollback_err) => {
|
||||
error!("ROLLBACK FAILED! fatal error: {:?}", rollback_err);
|
||||
TransactionResult::UnrecoverableError(commit_error(err), rollback_err)
|
||||
TransactionResult::UnrecoverableError(err, rollback_err)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -756,36 +714,15 @@ impl<'a> Clone for Step<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CommitError {
|
||||
CreateKubernetes(KubernetesError),
|
||||
DeleteKubernetes(KubernetesError),
|
||||
DeployEnvironment(KubernetesError),
|
||||
PauseEnvironment(KubernetesError),
|
||||
DeleteEnvironment(KubernetesError),
|
||||
NotValidService(ServiceError),
|
||||
BuildImage(BuildError),
|
||||
PushImage(PushError),
|
||||
DeployImage(DeployError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RollbackError {
|
||||
CreateKubernetes(KubernetesError),
|
||||
DeleteKubernetes(KubernetesError),
|
||||
DeployEnvironment(KubernetesError),
|
||||
PauseEnvironment(KubernetesError),
|
||||
DeleteEnvironment(KubernetesError),
|
||||
NotValidService(ServiceError),
|
||||
BuildImage(BuildError),
|
||||
PushImage(PushError),
|
||||
DeployImage(DeployError),
|
||||
CommitError(EngineError),
|
||||
NoFailoverEnvironment,
|
||||
Error,
|
||||
Nothing,
|
||||
}
|
||||
|
||||
pub enum TransactionResult {
|
||||
Ok,
|
||||
Rollback(CommitError),
|
||||
UnrecoverableError(CommitError, RollbackError),
|
||||
Rollback(EngineError),
|
||||
UnrecoverableError(EngineError, RollbackError),
|
||||
}
|
||||
|
||||
@@ -6,9 +6,8 @@ use qovery_engine::build_platform::GitRepository;
|
||||
use qovery_engine::cloud_provider::aws::kubernetes::node::Node;
|
||||
use qovery_engine::cloud_provider::aws::kubernetes::EKS;
|
||||
use qovery_engine::cloud_provider::aws::AWS;
|
||||
use qovery_engine::cloud_provider::kubernetes::{Kubernetes, KubernetesError};
|
||||
use qovery_engine::cloud_provider::kubernetes::Kubernetes;
|
||||
use qovery_engine::cloud_provider::CloudProvider;
|
||||
use qovery_engine::cmd::utilities::CmdError;
|
||||
use qovery_engine::dns_provider::cloudflare::Cloudflare;
|
||||
use qovery_engine::git::Credentials;
|
||||
use qovery_engine::models::{Clone2, GitCredentials};
|
||||
|
||||
@@ -30,8 +30,8 @@ fn delete_s3_bucket() {
|
||||
bucket_name,
|
||||
);
|
||||
match creation {
|
||||
Ok(out) => println!("Yippee Ki Yay"),
|
||||
Err(e) => println!("While creating the bucket {}", e),
|
||||
Ok(_) => println!("Yippee Ki Yay"),
|
||||
Err(e) => println!("While creating the bucket {}", e.message.unwrap()),
|
||||
}
|
||||
|
||||
let delete = delete_bucket(
|
||||
@@ -41,6 +41,6 @@ fn delete_s3_bucket() {
|
||||
);
|
||||
match delete {
|
||||
Ok(out) => println!("Yippee Ki Yay"),
|
||||
Err(e) => println!("While deleting the bucket {}", e),
|
||||
Err(e) => println!("While deleting the bucket {}", e.message.unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user