Refacto to have task status (#628)

This commit is contained in:
Erèbe - Romain Gerard
2022-03-08 10:23:47 +01:00
committed by GitHub
parent abab7874a3
commit 294686916d
18 changed files with 407 additions and 561 deletions

View File

@@ -1,7 +1,9 @@
use core::fmt;
use std::borrow::Borrow;
use std::env;
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use retry::delay::{Fibonacci, Fixed};
use retry::Error::Operation;
@@ -117,7 +119,7 @@ pub struct Options {
impl ProviderOptions for Options {}
pub struct EKS<'a> {
pub struct EKS {
context: Context,
id: String,
long_id: uuid::Uuid,
@@ -125,17 +127,17 @@ pub struct EKS<'a> {
version: String,
region: AwsRegion,
zones: Vec<AwsZones>,
cloud_provider: &'a dyn CloudProvider,
dns_provider: &'a dyn DnsProvider,
cloud_provider: Arc<Box<dyn CloudProvider>>,
dns_provider: Arc<Box<dyn DnsProvider>>,
s3: S3,
nodes_groups: Vec<NodeGroups>,
template_directory: String,
options: Options,
listeners: Listeners,
logger: &'a dyn Logger,
logger: Box<dyn Logger>,
}
impl<'a> EKS<'a> {
impl EKS {
pub fn new(
context: Context,
id: &str,
@@ -144,11 +146,11 @@ impl<'a> EKS<'a> {
version: &str,
region: AwsRegion,
zones: Vec<String>,
cloud_provider: &'a dyn CloudProvider,
dns_provider: &'a dyn DnsProvider,
cloud_provider: Arc<Box<dyn CloudProvider>>,
dns_provider: Arc<Box<dyn DnsProvider>>,
options: Options,
nodes_groups: Vec<NodeGroups>,
logger: &'a dyn Logger,
logger: Box<dyn Logger>,
) -> Result<Self, EngineError> {
let event_details = EventDetails::new(
Some(cloud_provider.kind()),
@@ -203,6 +205,8 @@ impl<'a> EKS<'a> {
context.resource_expiration_in_seconds(),
);
// copy listeners from CloudProvider
let listeners = cloud_provider.listeners().clone();
Ok(EKS {
context,
id: id.to_string(),
@@ -218,7 +222,7 @@ impl<'a> EKS<'a> {
nodes_groups,
template_directory,
logger,
listeners: cloud_provider.listeners().clone(), // copy listeners from CloudProvider
listeners,
})
}
@@ -1457,7 +1461,7 @@ impl<'a> EKS<'a> {
}
}
impl<'a> Kubernetes for EKS<'a> {
impl Kubernetes for EKS {
fn context(&self) -> &Context {
&self.context
}
@@ -1491,15 +1495,15 @@ impl<'a> Kubernetes for EKS<'a> {
}
fn cloud_provider(&self) -> &dyn CloudProvider {
self.cloud_provider
(*self.cloud_provider).borrow()
}
fn dns_provider(&self) -> &dyn DnsProvider {
self.dns_provider
(*self.dns_provider).borrow()
}
fn logger(&self) -> &dyn Logger {
self.logger
self.logger.borrow()
}
fn config_file_store(&self) -> &dyn ObjectStorage {
@@ -2002,7 +2006,7 @@ impl<'a> Kubernetes for EKS<'a> {
}
}
impl<'a> Listen for EKS<'a> {
impl Listen for EKS {
fn listeners(&self) -> &Listeners {
&self.listeners
}

View File

@@ -1,3 +1,4 @@
use std::borrow::Borrow;
use std::env;
use serde::{Deserialize, Serialize};
@@ -51,6 +52,7 @@ use retry::Error::Operation;
use retry::OperationResult;
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
pub mod cidr;
pub mod doks_api;
@@ -84,24 +86,24 @@ pub struct DoksOptions {
impl ProviderOptions for DoksOptions {}
pub struct DOKS<'a> {
pub struct DOKS {
context: Context,
id: String,
long_id: uuid::Uuid,
name: String,
version: String,
region: DoRegion,
cloud_provider: &'a dyn CloudProvider,
cloud_provider: Arc<Box<dyn CloudProvider>>,
nodes_groups: Vec<NodeGroups>,
dns_provider: &'a dyn DnsProvider,
dns_provider: Arc<Box<dyn DnsProvider>>,
spaces: Spaces,
template_directory: String,
options: DoksOptions,
listeners: Listeners,
logger: &'a dyn Logger,
logger: Box<dyn Logger>,
}
impl<'a> DOKS<'a> {
impl DOKS {
pub fn new(
context: Context,
id: String,
@@ -109,11 +111,11 @@ impl<'a> DOKS<'a> {
name: String,
version: String,
region: DoRegion,
cloud_provider: &'a dyn CloudProvider,
dns_provider: &'a dyn DnsProvider,
cloud_provider: Arc<Box<dyn CloudProvider>>,
dns_provider: Arc<Box<dyn DnsProvider>>,
nodes_groups: Vec<NodeGroups>,
options: DoksOptions,
logger: &'a dyn Logger,
logger: Box<dyn Logger>,
) -> Result<Self, EngineError> {
let template_directory = format!("{}/digitalocean/bootstrap", context.lib_root_dir());
@@ -149,6 +151,7 @@ impl<'a> DOKS<'a> {
BucketDeleteStrategy::HardDelete,
);
let listeners = cloud_provider.listeners().clone();
Ok(DOKS {
context,
id,
@@ -163,7 +166,7 @@ impl<'a> DOKS<'a> {
nodes_groups,
template_directory,
logger,
listeners: cloud_provider.listeners().clone(), // copy listeners from CloudProvider
listeners,
})
}
@@ -1304,7 +1307,7 @@ impl<'a> DOKS<'a> {
}
}
impl<'a> Kubernetes for DOKS<'a> {
impl Kubernetes for DOKS {
fn context(&self) -> &Context {
&self.context
}
@@ -1338,15 +1341,15 @@ impl<'a> Kubernetes for DOKS<'a> {
}
fn cloud_provider(&self) -> &dyn CloudProvider {
self.cloud_provider
self.cloud_provider.as_ref().borrow()
}
fn dns_provider(&self) -> &dyn DnsProvider {
self.dns_provider
self.dns_provider.as_ref().borrow()
}
fn logger(&self) -> &dyn Logger {
self.logger
self.logger.borrow()
}
fn config_file_store(&self) -> &dyn ObjectStorage {
@@ -1735,7 +1738,7 @@ impl<'a> Kubernetes for DOKS<'a> {
}
}
impl<'a> Listen for DOKS<'a> {
impl Listen for DOKS {
fn listeners(&self) -> &Listeners {
&self.listeners
}

View File

@@ -1,5 +1,4 @@
use crate::cloud_provider::service::{Action, StatefulService, StatelessService};
use crate::errors::EngineError;
use crate::unit_conversion::cpu_string_to_float;
pub struct Environment {
@@ -36,22 +35,6 @@ impl Environment {
self.namespace.as_str()
}
pub fn is_valid(&self) -> Result<(), EngineError> {
for service in self.stateful_services.iter() {
if let Err(err) = service.is_valid() {
return Err(err);
}
}
for service in self.stateless_services.iter() {
if let Err(err) = service.is_valid() {
return Err(err);
}
}
Ok(())
}
/// compute the required resources for this environment from
/// applications, external services, routers, and databases
/// Note: Even if external services don't run on the targeted Kubernetes cluster, it requires CPU and memory resources to run the container(s)

View File

@@ -40,9 +40,11 @@ use retry::OperationResult;
use scaleway_api_rs::apis::Error;
use scaleway_api_rs::models::ScalewayK8sV1Cluster;
use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
use std::env;
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use tera::Context as TeraContext;
#[derive(PartialEq)]
@@ -122,24 +124,24 @@ impl KapsuleOptions {
}
}
pub struct Kapsule<'a> {
pub struct Kapsule {
context: Context,
id: String,
long_id: uuid::Uuid,
name: String,
version: String,
zone: ScwZone,
cloud_provider: &'a dyn CloudProvider,
dns_provider: &'a dyn DnsProvider,
cloud_provider: Arc<Box<dyn CloudProvider>>,
dns_provider: Arc<Box<dyn DnsProvider>>,
object_storage: ScalewayOS,
nodes_groups: Vec<NodeGroups>,
template_directory: String,
options: KapsuleOptions,
listeners: Listeners,
logger: &'a dyn Logger,
logger: Box<dyn Logger>,
}
impl<'a> Kapsule<'a> {
impl Kapsule {
pub fn new(
context: Context,
id: String,
@@ -147,12 +149,12 @@ impl<'a> Kapsule<'a> {
name: String,
version: String,
zone: ScwZone,
cloud_provider: &'a dyn CloudProvider,
dns_provider: &'a dyn DnsProvider,
cloud_provider: Arc<Box<dyn CloudProvider>>,
dns_provider: Arc<Box<dyn DnsProvider>>,
nodes_groups: Vec<NodeGroups>,
options: KapsuleOptions,
logger: &'a dyn Logger,
) -> Result<Kapsule<'a>, EngineError> {
logger: Box<dyn Logger>,
) -> Result<Kapsule, EngineError> {
let template_directory = format!("{}/scaleway/bootstrap", context.lib_root_dir());
for node_group in &nodes_groups {
@@ -189,6 +191,7 @@ impl<'a> Kapsule<'a> {
context.resource_expiration_in_seconds(),
);
let listeners = cloud_provider.listeners().clone();
Ok(Kapsule {
context,
id,
@@ -203,7 +206,7 @@ impl<'a> Kapsule<'a> {
template_directory,
options,
logger,
listeners: cloud_provider.listeners().clone(), // copy listeners from CloudProvider
listeners,
})
}
@@ -1714,7 +1717,7 @@ impl<'a> Kapsule<'a> {
}
}
impl<'a> Kubernetes for Kapsule<'a> {
impl Kubernetes for Kapsule {
fn context(&self) -> &Context {
&self.context
}
@@ -1748,15 +1751,15 @@ impl<'a> Kubernetes for Kapsule<'a> {
}
fn cloud_provider(&self) -> &dyn CloudProvider {
self.cloud_provider
self.cloud_provider.as_ref().borrow()
}
fn dns_provider(&self) -> &dyn DnsProvider {
self.dns_provider
self.dns_provider.as_ref().borrow()
}
fn logger(&self) -> &dyn Logger {
self.logger
self.logger.borrow()
}
fn config_file_store(&self) -> &dyn ObjectStorage {
@@ -2122,7 +2125,7 @@ impl<'a> Kubernetes for Kapsule<'a> {
}
}
impl<'a> Listen for Kapsule<'a> {
impl Listen for Kapsule {
fn listeners(&self) -> &Listeners {
&self.listeners
}

View File

@@ -20,7 +20,7 @@ use crate::cmd::kubectl::ScalingKind::Statefulset;
use crate::cmd::kubectl::{kubectl_exec_delete_secret, kubectl_exec_scale_replicas_by_selector, ScalingKind};
use crate::cmd::structs::LabelsContent;
use crate::errors::{CommandError, EngineError};
use crate::events::{EngineEvent, EnvironmentStep, EventDetails, EventMessage, GeneralStep, Stage, ToTransmitter};
use crate::events::{EngineEvent, EnvironmentStep, EventDetails, EventMessage, Stage, ToTransmitter};
use crate::logger::{LogLevel, Logger};
use crate::models::ProgressLevel::Info;
use crate::models::{
@@ -106,22 +106,6 @@ pub trait Service: ToTransmitter {
TcpStream::connect(format!("{}:{}", ip, private_port)).is_ok()
}
fn is_valid(&self) -> Result<(), EngineError> {
let binaries = ["kubectl", "helm", "terraform", "aws-iam-authenticator"];
for binary in binaries.iter() {
if !crate::cmd::command::does_binary_exist(binary) {
return Err(EngineError::new_missing_required_binary(
self.get_event_details(Stage::General(GeneralStep::ValidateSystemRequirements)),
binary.to_string(),
));
}
}
// TODO check lib directories available
Ok(())
}
fn progress_scope(&self) -> ProgressScope {
let id = self.id().to_string();

View File

@@ -1,47 +1,46 @@
use std::borrow::Borrow;
use std::sync::Arc;
use crate::build_platform::BuildPlatform;
use crate::cloud_provider::kubernetes::Kubernetes;
use crate::cloud_provider::CloudProvider;
use crate::container_registry::ContainerRegistry;
use crate::dns_provider::DnsProvider;
use crate::errors::EngineError;
use crate::logger::Logger;
use crate::models::Context;
use crate::session::Session;
pub struct Engine {
pub struct EngineConfig {
context: Context,
build_platform: Box<dyn BuildPlatform>,
container_registry: Box<dyn ContainerRegistry>,
cloud_provider: Box<dyn CloudProvider>,
dns_provider: Box<dyn DnsProvider>,
logger: Box<dyn Logger>,
pub is_task_canceled: Box<dyn Fn() -> bool>,
cloud_provider: Arc<Box<dyn CloudProvider>>,
dns_provider: Arc<Box<dyn DnsProvider>>,
kubernetes: Box<dyn Kubernetes>,
}
impl Engine {
impl EngineConfig {
pub fn new(
context: Context,
build_platform: Box<dyn BuildPlatform>,
container_registry: Box<dyn ContainerRegistry>,
cloud_provider: Box<dyn CloudProvider>,
dns_provider: Box<dyn DnsProvider>,
logger: Box<dyn Logger>,
is_task_canceled: Box<dyn Fn() -> bool>,
) -> Engine {
Engine {
cloud_provider: Arc<Box<dyn CloudProvider>>,
dns_provider: Arc<Box<dyn DnsProvider>>,
kubernetes: Box<dyn Kubernetes>,
) -> EngineConfig {
EngineConfig {
context,
build_platform,
container_registry,
cloud_provider,
dns_provider,
logger,
is_task_canceled,
kubernetes,
}
}
}
impl<'a> Engine {
pub fn kubernetes(&self) -> &dyn Kubernetes {
self.kubernetes.as_ref()
}
pub fn context(&self) -> &Context {
&self.context
}
@@ -55,15 +54,11 @@ impl<'a> Engine {
}
pub fn cloud_provider(&self) -> &dyn CloudProvider {
self.cloud_provider.borrow()
(*self.cloud_provider).borrow()
}
pub fn dns_provider(&self) -> &dyn DnsProvider {
self.dns_provider.borrow()
}
pub fn logger(&self) -> &dyn Logger {
self.logger.borrow()
(*self.dns_provider).borrow()
}
pub fn is_valid(&self) -> Result<(), EngineError> {
@@ -74,12 +69,4 @@ impl<'a> Engine {
Ok(())
}
/// check and init the connection to all services
pub fn session(&'a self) -> Result<Session<'a>, EngineError> {
match self.is_valid() {
Ok(_) => Ok(Session::<'a> { engine: self }),
Err(err) => Err(err),
}
}
}

View File

@@ -23,7 +23,6 @@ pub mod logger;
pub mod models;
pub mod object_storage;
pub mod runtime;
pub mod session;
mod string;
mod template;
pub mod transaction;

View File

@@ -92,10 +92,6 @@ pub struct Environment {
}
impl Environment {
pub fn is_valid(&self) -> Result<(), EnvironmentError> {
Ok(())
}
pub fn to_qe_environment(
&self,
context: &Context,

View File

@@ -1,12 +0,0 @@
use crate::engine::Engine;
use crate::transaction::Transaction;
pub struct Session<'a> {
pub engine: &'a Engine,
}
impl<'a> Session<'a> {
pub fn transaction(self) -> Transaction<'a> {
Transaction::new(self.engine)
}
}

View File

@@ -5,7 +5,7 @@ use crate::build_platform::BuildResult;
use crate::cloud_provider::kubernetes::Kubernetes;
use crate::cloud_provider::service::{Application, Service};
use crate::container_registry::PushResult;
use crate::engine::Engine;
use crate::engine::EngineConfig;
use crate::errors::{EngineError, Tag};
use crate::events::{EngineEvent, EventMessage};
use crate::logger::{LogLevel, Logger};
@@ -15,57 +15,61 @@ use crate::models::{
};
pub struct Transaction<'a> {
engine: &'a Engine,
engine: &'a EngineConfig,
logger: Box<dyn Logger>,
steps: Vec<Step<'a>>,
executed_steps: Vec<Step<'a>>,
current_step: StepName,
is_transaction_aborted: Box<dyn Fn() -> bool>,
on_step_change: Box<dyn Fn(&StepName)>,
}
impl<'a> Transaction<'a> {
pub fn new(engine: &'a Engine) -> Self {
Transaction::<'a> {
pub fn new(
engine: &'a EngineConfig,
logger: Box<dyn Logger>,
is_transaction_aborted: Box<dyn Fn() -> bool>,
on_step_change: Box<dyn Fn(&StepName)>,
) -> Result<Self, EngineError> {
let _ = engine.is_valid()?;
let _ = engine.kubernetes().is_valid()?;
let mut tx = Transaction::<'a> {
engine,
logger,
steps: vec![],
executed_steps: vec![],
}
current_step: StepName::Waiting,
is_transaction_aborted,
on_step_change,
};
tx.set_current_step(StepName::Waiting);
Ok(tx)
}
pub fn create_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), EngineError> {
match kubernetes.is_valid() {
Ok(_) => {
self.steps.push(Step::CreateKubernetes(kubernetes));
Ok(())
}
Err(err) => Err(err),
}
pub fn set_current_step(&mut self, step: StepName) {
(self.on_step_change)(&step);
self.current_step = step;
}
pub fn pause_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), EngineError> {
match kubernetes.is_valid() {
Ok(_) => {
self.steps.push(Step::PauseKubernetes(kubernetes));
Ok(())
}
Err(err) => Err(err),
}
pub fn create_kubernetes(&mut self) -> Result<(), EngineError> {
self.steps.push(Step::CreateKubernetes);
Ok(())
}
pub fn delete_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), EngineError> {
match kubernetes.is_valid() {
Ok(_) => {
self.steps.push(Step::DeleteKubernetes(kubernetes));
Ok(())
}
Err(err) => Err(err),
}
pub fn pause_kubernetes(&mut self) -> Result<(), EngineError> {
self.steps.push(Step::PauseKubernetes);
Ok(())
}
pub fn deploy_environment(
&mut self,
kubernetes: &'a dyn Kubernetes,
environment_action: &'a EnvironmentAction,
) -> Result<(), EnvironmentError> {
pub fn delete_kubernetes(&mut self) -> Result<(), EngineError> {
self.steps.push(Step::DeleteKubernetes);
Ok(())
}
pub fn deploy_environment(&mut self, environment_action: &'a EnvironmentAction) -> Result<(), EnvironmentError> {
self.deploy_environment_with_options(
kubernetes,
environment_action,
DeploymentOption {
force_build: false,
@@ -76,51 +80,25 @@ impl<'a> Transaction<'a> {
pub fn deploy_environment_with_options(
&mut self,
kubernetes: &'a dyn Kubernetes,
environment_action: &'a EnvironmentAction,
option: DeploymentOption,
) -> Result<(), EnvironmentError> {
let _ = self.check_environment_action(environment_action)?;
// add build step
self.steps.push(Step::BuildEnvironment(environment_action, option));
// add deployment step
self.steps.push(Step::DeployEnvironment(kubernetes, environment_action));
self.steps.push(Step::DeployEnvironment(environment_action));
Ok(())
}
pub fn pause_environment(
&mut self,
kubernetes: &'a dyn Kubernetes,
environment_action: &'a EnvironmentAction,
) -> Result<(), EnvironmentError> {
let _ = self.check_environment_action(environment_action)?;
self.steps.push(Step::PauseEnvironment(kubernetes, environment_action));
pub fn pause_environment(&mut self, environment_action: &'a EnvironmentAction) -> Result<(), EnvironmentError> {
self.steps.push(Step::PauseEnvironment(environment_action));
Ok(())
}
pub fn delete_environment(
&mut self,
kubernetes: &'a dyn Kubernetes,
environment_action: &'a EnvironmentAction,
) -> Result<(), EnvironmentError> {
let _ = self.check_environment_action(environment_action)?;
self.steps.push(Step::DeleteEnvironment(kubernetes, environment_action));
Ok(())
}
fn check_environment_action(&self, environment_action: &EnvironmentAction) -> Result<(), EnvironmentError> {
match environment_action {
EnvironmentAction::Environment(te) => match te.is_valid() {
Ok(_) => {}
Err(err) => return Err(err),
},
};
pub fn delete_environment(&mut self, environment_action: &'a EnvironmentAction) -> Result<(), EnvironmentError> {
self.steps.push(Step::DeleteEnvironment(environment_action));
Ok(())
}
@@ -153,7 +131,6 @@ impl<'a> Transaction<'a> {
&self,
environment: &Environment,
option: &DeploymentOption,
logger: Box<dyn Logger>,
) -> Result<Vec<Box<dyn Application>>, EngineError> {
// do the same for applications
let apps_to_build = environment
@@ -171,11 +148,9 @@ impl<'a> Transaction<'a> {
let _ = self.load_build_app_cache(app);
// only if the build is forced OR if the image does not exist in the registry
self.engine.build_platform().build(
app.to_build(),
option.force_build,
&self.engine.is_task_canceled,
)
self.engine
.build_platform()
.build(app.to_build(), option.force_build, &self.is_transaction_aborted)
} else {
// use the cache
Ok(BuildResult::new(app.to_build()))
@@ -200,7 +175,7 @@ impl<'a> Transaction<'a> {
self.engine.context(),
&build_result.build.image,
self.engine.cloud_provider(),
logger.clone(),
self.logger.clone(),
) {
applications.push(app)
}
@@ -242,58 +217,39 @@ impl<'a> Transaction<'a> {
Ok(results)
}
fn check_environment(
&self,
environment: &crate::cloud_provider::environment::Environment,
logger: Box<dyn Logger>,
) -> TransactionResult {
if let Err(engine_error) = environment.is_valid() {
warn!("ROLLBACK STARTED! an error occurred {:?}", engine_error);
return match self.rollback(logger) {
Ok(_) => TransactionResult::Rollback(engine_error),
Err(err) => {
error!("ROLLBACK FAILED! fatal error: {:?}", err);
TransactionResult::UnrecoverableError(engine_error, err)
}
};
};
TransactionResult::Ok
}
pub fn rollback(&self, logger: Box<dyn Logger>) -> Result<(), RollbackError> {
pub fn rollback(&self) -> Result<(), RollbackError> {
for step in self.executed_steps.iter() {
match step {
Step::CreateKubernetes(kubernetes) => {
Step::CreateKubernetes => {
// revert kubernetes creation
if let Err(err) = kubernetes.on_create_error() {
if let Err(err) = self.engine.kubernetes().on_create_error() {
return Err(RollbackError::CommitError(err));
};
}
Step::DeleteKubernetes(kubernetes) => {
Step::DeleteKubernetes => {
// revert kubernetes deletion
if let Err(err) = kubernetes.on_delete_error() {
if let Err(err) = self.engine.kubernetes().on_delete_error() {
return Err(RollbackError::CommitError(err));
};
}
Step::PauseKubernetes(kubernetes) => {
Step::PauseKubernetes => {
// revert pause
if let Err(err) = kubernetes.on_pause_error() {
if let Err(err) = self.engine.kubernetes().on_pause_error() {
return Err(RollbackError::CommitError(err));
};
}
Step::BuildEnvironment(_environment_action, _option) => {
// revert build applications
}
Step::DeployEnvironment(kubernetes, environment_action) => {
Step::DeployEnvironment(environment_action) => {
// revert environment deployment
self.rollback_environment(*kubernetes, *environment_action, logger.clone())?;
self.rollback_environment(*environment_action)?;
}
Step::PauseEnvironment(kubernetes, environment_action) => {
self.rollback_environment(*kubernetes, *environment_action, logger.clone())?;
Step::PauseEnvironment(environment_action) => {
self.rollback_environment(*environment_action)?;
}
Step::DeleteEnvironment(kubernetes, environment_action) => {
self.rollback_environment(*kubernetes, *environment_action, logger.clone())?;
Step::DeleteEnvironment(environment_action) => {
self.rollback_environment(*environment_action)?;
}
}
}
@@ -303,12 +259,7 @@ impl<'a> Transaction<'a> {
/// This function is a wrapper to correctly revert all changes of an attempted deployment AND
/// if a failover environment is provided, then rollback.
fn rollback_environment(
&self,
kubernetes: &dyn Kubernetes,
environment_action: &EnvironmentAction,
logger: Box<dyn Logger>,
) -> Result<(), RollbackError> {
fn rollback_environment(&self, environment_action: &EnvironmentAction) -> Result<(), RollbackError> {
let qe_environment = |environment: &Environment| {
let mut _applications = Vec::with_capacity(environment.applications.len());
for application in environment.applications.iter() {
@@ -318,7 +269,7 @@ impl<'a> Transaction<'a> {
self.engine.context(),
&build.image,
self.engine.cloud_provider(),
logger.clone(),
self.logger.clone(),
) {
_applications.push(x)
}
@@ -328,7 +279,7 @@ impl<'a> Transaction<'a> {
self.engine.context(),
&_applications,
self.engine.cloud_provider(),
logger.clone(),
self.logger.clone(),
);
qe_environment
@@ -340,9 +291,15 @@ impl<'a> Transaction<'a> {
let target_qe_environment = qe_environment(te);
let action = match te.action {
Action::Create => kubernetes.deploy_environment_error(&target_qe_environment),
Action::Pause => kubernetes.pause_environment_error(&target_qe_environment),
Action::Delete => kubernetes.delete_environment_error(&target_qe_environment),
Action::Create => self
.engine
.kubernetes()
.deploy_environment_error(&target_qe_environment),
Action::Pause => self.engine.kubernetes().pause_environment_error(&target_qe_environment),
Action::Delete => self
.engine
.kubernetes()
.delete_environment_error(&target_qe_environment),
Action::Nothing => Ok(()),
};
@@ -356,22 +313,18 @@ impl<'a> Transaction<'a> {
}
}
pub fn commit(mut self, logger: Box<dyn Logger>) -> TransactionResult {
pub fn commit(mut self) -> TransactionResult {
let mut applications_by_environment: HashMap<&Environment, Vec<Box<dyn Application>>> = HashMap::new();
for step in self.steps.iter() {
for step in self.steps.clone().into_iter() {
// execution loop
self.executed_steps.push(step.clone());
self.set_current_step(step.step_name());
match step {
Step::CreateKubernetes(kubernetes) => {
Step::CreateKubernetes => {
// create kubernetes
match self.commit_infrastructure(
*kubernetes,
Action::Create,
kubernetes.on_create(),
logger.clone(),
) {
match self.commit_infrastructure(Action::Create, self.engine.kubernetes().on_create()) {
TransactionResult::Ok => {}
err => {
error!("Error while creating infrastructure: {:?}", err);
@@ -379,14 +332,9 @@ impl<'a> Transaction<'a> {
}
};
}
Step::DeleteKubernetes(kubernetes) => {
Step::DeleteKubernetes => {
// delete kubernetes
match self.commit_infrastructure(
*kubernetes,
Action::Delete,
kubernetes.on_delete(),
logger.clone(),
) {
match self.commit_infrastructure(Action::Delete, self.engine.kubernetes().on_delete()) {
TransactionResult::Ok => {}
err => {
error!("Error while deleting infrastructure: {:?}", err);
@@ -394,10 +342,9 @@ impl<'a> Transaction<'a> {
}
};
}
Step::PauseKubernetes(kubernetes) => {
Step::PauseKubernetes => {
// pause kubernetes
match self.commit_infrastructure(*kubernetes, Action::Pause, kubernetes.on_pause(), logger.clone())
{
match self.commit_infrastructure(Action::Pause, self.engine.kubernetes().on_pause()) {
TransactionResult::Ok => {}
err => {
error!("Error while pausing infrastructure: {:?}", err);
@@ -411,11 +358,10 @@ impl<'a> Transaction<'a> {
EnvironmentAction::Environment(te) => te,
};
let applications_builds = match self.build_applications(target_environment, option, logger.clone())
{
let applications_builds = match self.build_applications(target_environment, &option) {
Ok(apps) => apps,
Err(engine_err) => {
logger.log(
self.logger.log(
LogLevel::Error,
EngineEvent::Error(
engine_err.clone(),
@@ -433,11 +379,11 @@ impl<'a> Transaction<'a> {
}
};
if (self.engine.is_task_canceled)() {
if (self.is_transaction_aborted)() {
return TransactionResult::Canceled;
}
let applications = match self.push_applications(applications_builds, option) {
let applications = match self.push_applications(applications_builds, &option) {
Ok(results) => {
let applications = results.into_iter().map(|(app, _)| app).collect::<Vec<_>>();
@@ -445,7 +391,7 @@ impl<'a> Transaction<'a> {
}
Err(engine_err) => {
warn!("ROLLBACK STARTED! an error occurred {:?}", engine_err);
return match self.rollback(logger.clone()) {
return match self.rollback() {
Ok(_) => TransactionResult::Rollback(engine_err),
Err(err) => {
error!("ROLLBACK FAILED! fatal error: {:?}", err);
@@ -457,15 +403,11 @@ impl<'a> Transaction<'a> {
applications_by_environment.insert(target_environment, applications);
}
Step::DeployEnvironment(kubernetes, environment_action) => {
Step::DeployEnvironment(environment_action) => {
// deploy complete environment
match self.commit_environment(
*kubernetes,
*environment_action,
&applications_by_environment,
|qe_env| kubernetes.deploy_environment(qe_env),
logger.clone(),
) {
match self.commit_environment(environment_action, &applications_by_environment, |qe_env| {
self.engine.kubernetes().deploy_environment(qe_env)
}) {
TransactionResult::Ok => {}
err => {
error!("Error while deploying environment: {:?}", err);
@@ -473,15 +415,11 @@ impl<'a> Transaction<'a> {
}
};
}
Step::PauseEnvironment(kubernetes, environment_action) => {
Step::PauseEnvironment(environment_action) => {
// pause complete environment
match self.commit_environment(
*kubernetes,
*environment_action,
&applications_by_environment,
|qe_env| kubernetes.pause_environment(qe_env),
logger.clone(),
) {
match self.commit_environment(environment_action, &applications_by_environment, |qe_env| {
self.engine.kubernetes().pause_environment(qe_env)
}) {
TransactionResult::Ok => {}
err => {
error!("Error while pausing environment: {:?}", err);
@@ -489,15 +427,11 @@ impl<'a> Transaction<'a> {
}
};
}
Step::DeleteEnvironment(kubernetes, environment_action) => {
Step::DeleteEnvironment(environment_action) => {
// delete complete environment
match self.commit_environment(
*kubernetes,
*environment_action,
&applications_by_environment,
|qe_env| kubernetes.delete_environment(qe_env),
logger.clone(),
) {
match self.commit_environment(environment_action, &applications_by_environment, |qe_env| {
self.engine.kubernetes().delete_environment(qe_env)
}) {
TransactionResult::Ok => {}
err => {
error!("Error while deleting environment: {:?}", err);
@@ -511,13 +445,7 @@ impl<'a> Transaction<'a> {
TransactionResult::Ok
}
fn commit_infrastructure(
&self,
kubernetes: &dyn Kubernetes,
action: Action,
result: Result<(), EngineError>,
logger: Box<dyn Logger>,
) -> TransactionResult {
fn commit_infrastructure(&self, action: Action, result: Result<(), EngineError>) -> TransactionResult {
// send back the right progress status
fn send_progress(lh: &ListenersHelper, action: Action, execution_id: &str, is_error: bool) {
let progress_info = ProgressInfo::new(
@@ -548,7 +476,7 @@ impl<'a> Transaction<'a> {
}
let execution_id = self.engine.context().execution_id();
let lh = ListenersHelper::new(kubernetes.listeners());
let lh = ListenersHelper::new(self.engine.kubernetes().listeners());
// 100 ms sleep to avoid race condition on last service status update
// Otherwise, the last status sent to the CORE is (sometimes) not the right one.
@@ -558,7 +486,7 @@ impl<'a> Transaction<'a> {
match result {
Err(err) => {
warn!("infrastructure ROLLBACK STARTED! an error occurred {:?}", err);
match self.rollback(logger) {
match self.rollback() {
Ok(_) => {
// an error occurred on infrastructure deployment BUT rolledback is OK
send_progress(&lh, action, execution_id, true);
@@ -582,11 +510,9 @@ impl<'a> Transaction<'a> {
fn commit_environment<F>(
&self,
kubernetes: &dyn Kubernetes,
environment_action: &EnvironmentAction,
applications_by_environment: &HashMap<&Environment, Vec<Box<dyn Application>>>,
action_fn: F,
logger: Box<dyn Logger>,
) -> TransactionResult
where
F: Fn(&crate::cloud_provider::environment::Environment) -> Result<(), EngineError>,
@@ -604,15 +530,10 @@ impl<'a> Transaction<'a> {
let qe_environment = target_environment.to_qe_environment(
self.engine.context(),
built_applications,
kubernetes.cloud_provider(),
logger.clone(),
self.engine.cloud_provider(),
self.logger.clone(),
);
let _ = match self.check_environment(&qe_environment, logger.clone()) {
TransactionResult::Ok => {}
err => return err, // which it means that an error occurred
};
let execution_id = self.engine.context().execution_id();
// send back the right progress status
@@ -658,7 +579,7 @@ impl<'a> Transaction<'a> {
let _ = match action_fn(&qe_environment) {
Err(err) => {
let rollback_result = match self.rollback(logger) {
let rollback_result = match self.rollback() {
Ok(_) => TransactionResult::Rollback(err),
Err(rollback_err) => {
error!("ROLLBACK FAILED! fatal error: {:?}", rollback_err);
@@ -669,11 +590,23 @@ impl<'a> Transaction<'a> {
// !!! don't change the order
// terminal update
for service in &qe_environment.stateful_services {
send_progress(kubernetes, &target_environment.action, service, execution_id, true);
send_progress(
self.engine.kubernetes(),
&target_environment.action,
service,
execution_id,
true,
);
}
for service in &qe_environment.stateless_services {
send_progress(kubernetes, &target_environment.action, service, execution_id, true);
send_progress(
self.engine.kubernetes(),
&target_environment.action,
service,
execution_id,
true,
);
}
return rollback_result;
@@ -681,11 +614,23 @@ impl<'a> Transaction<'a> {
_ => {
// terminal update
for service in &qe_environment.stateful_services {
send_progress(kubernetes, &target_environment.action, service, execution_id, false);
send_progress(
self.engine.kubernetes(),
&target_environment.action,
service,
execution_id,
false,
);
}
for service in &qe_environment.stateless_services {
send_progress(kubernetes, &target_environment.action, service, execution_id, false);
send_progress(
self.engine.kubernetes(),
&target_environment.action,
service,
execution_id,
false,
);
}
}
};
@@ -700,27 +645,68 @@ pub struct DeploymentOption {
pub force_push: bool,
}
enum Step<'a> {
#[derive(Clone)]
pub enum StepName {
CreateKubernetes,
DeleteKubernetes,
PauseKubernetes,
BuildEnvironment,
DeployEnvironment,
PauseEnvironment,
DeleteEnvironment,
Waiting,
}
impl StepName {
pub fn can_be_canceled(&self) -> bool {
match self {
StepName::CreateKubernetes => false,
StepName::DeleteKubernetes => false,
StepName::PauseKubernetes => false,
StepName::DeployEnvironment => false,
StepName::PauseEnvironment => false,
StepName::DeleteEnvironment => false,
StepName::BuildEnvironment => true,
StepName::Waiting => true,
}
}
}
pub enum Step<'a> {
// init and create all the necessary resources (Network, Kubernetes)
CreateKubernetes(&'a dyn Kubernetes),
DeleteKubernetes(&'a dyn Kubernetes),
PauseKubernetes(&'a dyn Kubernetes),
CreateKubernetes,
DeleteKubernetes,
PauseKubernetes,
BuildEnvironment(&'a EnvironmentAction, DeploymentOption),
DeployEnvironment(&'a dyn Kubernetes, &'a EnvironmentAction),
PauseEnvironment(&'a dyn Kubernetes, &'a EnvironmentAction),
DeleteEnvironment(&'a dyn Kubernetes, &'a EnvironmentAction),
DeployEnvironment(&'a EnvironmentAction),
PauseEnvironment(&'a EnvironmentAction),
DeleteEnvironment(&'a EnvironmentAction),
}
impl<'a> Step<'a> {
fn step_name(&self) -> StepName {
match self {
Step::CreateKubernetes => StepName::CreateKubernetes,
Step::DeleteKubernetes => StepName::DeleteKubernetes,
Step::PauseKubernetes => StepName::PauseKubernetes,
Step::BuildEnvironment(_, _) => StepName::BuildEnvironment,
Step::DeployEnvironment(_) => StepName::DeployEnvironment,
Step::PauseEnvironment(_) => StepName::PauseEnvironment,
Step::DeleteEnvironment(_) => StepName::DeleteEnvironment,
}
}
}
impl<'a> Clone for Step<'a> {
fn clone(&self) -> Self {
match self {
Step::CreateKubernetes(k) => Step::CreateKubernetes(*k),
Step::DeleteKubernetes(k) => Step::DeleteKubernetes(*k),
Step::PauseKubernetes(k) => Step::PauseKubernetes(*k),
Step::CreateKubernetes => Step::CreateKubernetes,
Step::DeleteKubernetes => Step::DeleteKubernetes,
Step::PauseKubernetes => Step::PauseKubernetes,
Step::BuildEnvironment(e, option) => Step::BuildEnvironment(*e, option.clone()),
Step::DeployEnvironment(k, e) => Step::DeployEnvironment(*k, *e),
Step::PauseEnvironment(k, e) => Step::PauseEnvironment(*k, *e),
Step::DeleteEnvironment(k, e) => Step::DeleteEnvironment(*k, *e),
Step::DeployEnvironment(e) => Step::DeployEnvironment(*e),
Step::PauseEnvironment(e) => Step::PauseEnvironment(*e),
Step::DeleteEnvironment(e) => Step::DeleteEnvironment(*e),
}
}
}

View File

@@ -7,17 +7,20 @@ use qovery_engine::cloud_provider::aws::regions::AwsRegion;
use qovery_engine::cloud_provider::aws::AWS;
use qovery_engine::cloud_provider::models::NodeGroups;
use qovery_engine::cloud_provider::qovery::EngineLocation::ClientSide;
use qovery_engine::cloud_provider::TerraformStateCredentials;
use qovery_engine::cloud_provider::Kind::Aws;
use qovery_engine::cloud_provider::{CloudProvider, TerraformStateCredentials};
use qovery_engine::container_registry::docker_hub::DockerHub;
use qovery_engine::container_registry::ecr::ECR;
use qovery_engine::engine::Engine;
use qovery_engine::dns_provider::DnsProvider;
use qovery_engine::engine::EngineConfig;
use qovery_engine::logger::Logger;
use qovery_engine::models::Context;
use std::str::FromStr;
use std::sync::Arc;
use tracing::error;
use crate::cloudflare::dns_provider_cloudflare;
use crate::common::{Cluster, ClusterDomain};
use crate::common::{get_environment_test_kubernetes, Cluster, ClusterDomain};
use crate::utilities::{build_platform_local_docker, FuncTestsSecrets};
pub const AWS_REGION_FOR_S3: AwsRegion = AwsRegion::EuWest3;
@@ -61,7 +64,7 @@ pub fn container_registry_docker_hub(context: &Context) -> DockerHub {
}
impl Cluster<AWS, Options> for AWS {
fn docker_cr_engine(context: &Context, logger: Box<dyn Logger>) -> Engine {
fn docker_cr_engine(context: &Context, logger: Box<dyn Logger>) -> EngineConfig {
// use ECR
let container_registry = Box::new(container_registry_ecr(context));
@@ -69,18 +72,25 @@ impl Cluster<AWS, Options> for AWS {
let build_platform = Box::new(build_platform_local_docker(context, logger.clone()));
// use AWS
let cloud_provider = AWS::cloud_provider(context);
let cloud_provider: Arc<Box<dyn CloudProvider>> = Arc::new(AWS::cloud_provider(context));
let dns_provider: Arc<Box<dyn DnsProvider>> =
Arc::new(dns_provider_cloudflare(context, ClusterDomain::Default));
let dns_provider = Box::new(dns_provider_cloudflare(context, ClusterDomain::Default));
let k = get_environment_test_kubernetes(
Aws,
context,
cloud_provider.clone(),
dns_provider.clone(),
logger.clone(),
);
Engine::new(
EngineConfig::new(
context.clone(),
build_platform,
container_registry,
cloud_provider,
dns_provider,
logger,
Box::new(|| false),
k,
)
}

View File

@@ -1,20 +1,21 @@
use crate::common::ClusterDomain;
use crate::utilities::FuncTestsSecrets;
use qovery_engine::dns_provider::cloudflare::Cloudflare;
use qovery_engine::dns_provider::DnsProvider;
use qovery_engine::models::{Context, Domain};
pub fn dns_provider_cloudflare(context: &Context, domain: ClusterDomain) -> Cloudflare {
pub fn dns_provider_cloudflare(context: &Context, domain: ClusterDomain) -> Box<dyn DnsProvider> {
let secrets = FuncTestsSecrets::new();
let domain = Domain::new(match domain {
ClusterDomain::Custom(domain) => domain,
ClusterDomain::Default => secrets.CLOUDFLARE_DOMAIN.expect("CLOUDFLARE_DOMAIN is not set"),
});
Cloudflare::new(
Box::new(Cloudflare::new(
context.clone(),
"qoverytestdnsclo",
"Qovery Test Cloudflare",
domain,
secrets.CLOUDFLARE_TOKEN.expect("CLOUDFLARE_TOKEN is not set").as_str(), // Cloudflare name: Qovery test
secrets.CLOUDFLARE_ID.expect("CLOUDFLARE_ID is not set").as_str(),
)
))
}

View File

@@ -9,7 +9,6 @@ use qovery_engine::models::{
Action, Application, Clone2, Context, Database, DatabaseKind, DatabaseMode, Environment, EnvironmentAction,
GitCredentials, Port, Protocol, Route, Router, Storage, StorageType,
};
use qovery_engine::transaction::TransactionResult;
use crate::aws::AWS_KUBERNETES_VERSION;
use crate::cloudflare::dns_provider_cloudflare;
@@ -34,14 +33,15 @@ use qovery_engine::cloud_provider::scaleway::Scaleway;
use qovery_engine::cloud_provider::{CloudProvider, Kind};
use qovery_engine::cmd::kubectl::kubernetes_get_all_hpas;
use qovery_engine::cmd::structs::SVCItem;
use qovery_engine::engine::Engine;
use qovery_engine::engine::EngineConfig;
use qovery_engine::errors::CommandError;
use qovery_engine::logger::Logger;
use qovery_engine::models::DatabaseMode::CONTAINER;
use qovery_engine::transaction::DeploymentOption;
use qovery_engine::transaction::{DeploymentOption, Transaction, TransactionResult};
use std::collections::BTreeMap;
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use tracing::{span, Level};
pub enum RegionActivationStatus {
@@ -55,7 +55,7 @@ pub enum ClusterDomain {
}
pub trait Cluster<T, U> {
fn docker_cr_engine(context: &Context, logger: Box<dyn Logger>) -> Engine;
fn docker_cr_engine(context: &Context, logger: Box<dyn Logger>) -> EngineConfig;
fn cloud_provider(context: &Context) -> Box<T>;
fn kubernetes_nodes() -> Vec<NodeGroups>;
fn kubernetes_cluster_options(secrets: FuncTestsSecrets, cluster_id: Option<String>) -> U;
@@ -93,26 +93,14 @@ impl Infrastructure for Environment {
environment_action: &EnvironmentAction,
logger: Box<dyn Logger>,
) -> TransactionResult {
let engine: Engine = match provider_kind {
let engine: EngineConfig = match provider_kind {
Kind::Aws => AWS::docker_cr_engine(context, logger.clone()),
Kind::Do => DO::docker_cr_engine(context, logger.clone()),
Kind::Scw => Scaleway::docker_cr_engine(context, logger.clone()),
};
let session = engine.session().unwrap();
let mut tx = session.transaction();
let dns_provider = dns_provider_cloudflare(context, ClusterDomain::Default);
let cp: Box<dyn CloudProvider>;
cp = match provider_kind {
Kind::Aws => AWS::cloud_provider(context),
Kind::Do => DO::cloud_provider(context),
Kind::Scw => Scaleway::cloud_provider(context),
};
let k;
k = get_environment_test_kubernetes(provider_kind, context, cp.as_ref(), &dns_provider, logger.as_ref());
let mut tx = Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
let _ = tx.deploy_environment_with_options(
k.as_ref(),
&environment_action,
DeploymentOption {
force_build: true,
@@ -120,7 +108,7 @@ impl Infrastructure for Environment {
},
);
tx.commit(logger.clone())
tx.commit()
}
fn pause_environment(
@@ -130,28 +118,16 @@ impl Infrastructure for Environment {
environment_action: &EnvironmentAction,
logger: Box<dyn Logger>,
) -> TransactionResult {
let engine: Engine = match provider_kind {
let engine: EngineConfig = match provider_kind {
Kind::Aws => AWS::docker_cr_engine(context, logger.clone()),
Kind::Do => DO::docker_cr_engine(context, logger.clone()),
Kind::Scw => Scaleway::docker_cr_engine(context, logger.clone()),
};
let session = engine.session().unwrap();
let mut tx = session.transaction();
let mut tx = Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
let _ = tx.pause_environment(&environment_action);
let dns_provider = dns_provider_cloudflare(context, ClusterDomain::Default);
let cp: Box<dyn CloudProvider>;
cp = match provider_kind {
Kind::Aws => AWS::cloud_provider(context),
Kind::Do => DO::cloud_provider(context),
Kind::Scw => Scaleway::cloud_provider(context),
};
let k;
k = get_environment_test_kubernetes(provider_kind, context, cp.as_ref(), &dns_provider, logger.as_ref());
let _ = tx.pause_environment(k.as_ref(), &environment_action);
tx.commit(logger.clone())
tx.commit()
}
fn delete_environment(
@@ -161,28 +137,16 @@ impl Infrastructure for Environment {
environment_action: &EnvironmentAction,
logger: Box<dyn Logger>,
) -> TransactionResult {
let engine: Engine = match provider_kind {
let engine: EngineConfig = match provider_kind {
Kind::Aws => AWS::docker_cr_engine(context, logger.clone()),
Kind::Do => DO::docker_cr_engine(context, logger.clone()),
Kind::Scw => Scaleway::docker_cr_engine(context, logger.clone()),
};
let session = engine.session().unwrap();
let mut tx = session.transaction();
let mut tx = Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
let _ = tx.delete_environment(&environment_action);
let dns_provider = dns_provider_cloudflare(context, ClusterDomain::Default);
let cp: Box<dyn CloudProvider>;
cp = match provider_kind {
Kind::Aws => AWS::cloud_provider(context),
Kind::Do => DO::cloud_provider(context),
Kind::Scw => Scaleway::cloud_provider(context),
};
let k;
k = get_environment_test_kubernetes(provider_kind, context, cp.as_ref(), &dns_provider, logger.as_ref());
let _ = tx.delete_environment(k.as_ref(), &environment_action);
tx.commit(logger.clone())
tx.commit()
}
}
@@ -1149,10 +1113,10 @@ pub fn test_db(
pub fn get_environment_test_kubernetes<'a>(
provider_kind: Kind,
context: &Context,
cloud_provider: &'a dyn CloudProvider,
dns_provider: &'a dyn DnsProvider,
logger: &'a dyn Logger,
) -> Box<dyn Kubernetes + 'a> {
cloud_provider: Arc<Box<dyn CloudProvider>>,
dns_provider: Arc<Box<dyn DnsProvider>>,
logger: Box<dyn Logger>,
) -> Box<dyn Kubernetes> {
let secrets = FuncTestsSecrets::new();
let k: Box<dyn Kubernetes>;
@@ -1246,10 +1210,10 @@ pub fn get_cluster_test_kubernetes<'a>(
boot_version: String,
localisation: &str,
aws_zones: Option<Vec<AwsZones>>,
cloud_provider: &'a dyn CloudProvider,
dns_provider: &'a dyn DnsProvider,
cloud_provider: Arc<Box<dyn CloudProvider>>,
dns_provider: Arc<Box<dyn DnsProvider>>,
vpc_network_mode: Option<VpcQoveryNetworkMode>,
logger: &'a dyn Logger,
logger: Box<dyn Logger>,
) -> Box<dyn Kubernetes + 'a> {
let k: Box<dyn Kubernetes>;
@@ -1342,22 +1306,13 @@ pub fn cluster_test(
let cluster_name = generate_cluster_id(localisation.clone());
let boot_version = format!("{}.{}", major_boot_version, minor_boot_version.clone());
let engine;
match provider_kind {
Kind::Aws => engine = AWS::docker_cr_engine(&context, logger.clone()),
Kind::Do => engine = DO::docker_cr_engine(&context, logger.clone()),
Kind::Scw => engine = Scaleway::docker_cr_engine(&context, logger.clone()),
};
let dns_provider = dns_provider_cloudflare(&context, cluster_domain);
let mut deploy_tx = engine.session().unwrap().transaction();
let mut delete_tx = engine.session().unwrap().transaction();
let cp: Box<dyn CloudProvider>;
cp = match provider_kind {
Kind::Aws => AWS::cloud_provider(&context),
Kind::Do => DO::cloud_provider(&context),
Kind::Scw => Scaleway::cloud_provider(&context),
let engine = match provider_kind {
Kind::Aws => AWS::docker_cr_engine(&context, logger.clone()),
Kind::Do => DO::docker_cr_engine(&context, logger.clone()),
Kind::Scw => Scaleway::docker_cr_engine(&context, logger.clone()),
};
let mut deploy_tx = Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
let mut delete_tx = Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
let mut aws_zones_string: Vec<String> = Vec::with_capacity(3);
if aws_zones.is_some() {
@@ -1366,6 +1321,13 @@ pub fn cluster_test(
}
};
let dns_provider = Arc::new(dns_provider_cloudflare(&context, cluster_domain));
let cp: Arc<Box<dyn CloudProvider>> = match provider_kind {
Kind::Aws => Arc::new(AWS::cloud_provider(&context)),
Kind::Do => Arc::new(DO::cloud_provider(&context)),
Kind::Scw => Arc::new(Scaleway::cloud_provider(&context)),
};
let kubernetes = get_cluster_test_kubernetes(
provider_kind.clone(),
secrets.clone(),
@@ -1375,28 +1337,29 @@ pub fn cluster_test(
boot_version.clone(),
localisation.clone(),
aws_zones.clone(),
cp.as_ref(),
&dns_provider,
cp.clone(),
dns_provider.clone(),
vpc_network_mode.clone(),
logger.as_ref(),
logger.clone(),
);
// Deploy
if let Err(err) = deploy_tx.create_kubernetes(kubernetes.as_ref()) {
if let Err(err) = deploy_tx.create_kubernetes() {
panic!("{:?}", err)
}
assert!(matches!(deploy_tx.commit(logger.clone()), TransactionResult::Ok));
assert!(matches!(deploy_tx.commit(), TransactionResult::Ok));
// Deploy env if any
if let Some(env) = environment_to_deploy {
let mut deploy_env_tx = engine.session().unwrap().transaction();
let mut deploy_env_tx =
Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
// Deploy env
if let Err(err) = deploy_env_tx.deploy_environment(kubernetes.as_ref(), env) {
if let Err(err) = deploy_env_tx.deploy_environment(env) {
panic!("{:?}", err)
}
assert!(matches!(deploy_env_tx.commit(logger.clone()), TransactionResult::Ok));
assert!(matches!(deploy_env_tx.commit(), TransactionResult::Ok));
}
if let Err(err) = metrics_server_test(
@@ -1411,21 +1374,22 @@ pub fn cluster_test(
match test_type {
ClusterTestType::Classic => {}
ClusterTestType::WithPause => {
let mut pause_tx = engine.session().unwrap().transaction();
let mut resume_tx = engine.session().unwrap().transaction();
let mut pause_tx = Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
let mut resume_tx =
Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
// Pause
if let Err(err) = pause_tx.pause_kubernetes(kubernetes.as_ref()) {
if let Err(err) = pause_tx.pause_kubernetes() {
panic!("{:?}", err)
}
assert!(matches!(pause_tx.commit(logger.clone()), TransactionResult::Ok));
assert!(matches!(pause_tx.commit(), TransactionResult::Ok));
// Resume
if let Err(err) = resume_tx.create_kubernetes(kubernetes.as_ref()) {
if let Err(err) = resume_tx.create_kubernetes() {
panic!("{:?}", err)
}
assert!(matches!(resume_tx.commit(logger.clone()), TransactionResult::Ok));
assert!(matches!(resume_tx.commit(), TransactionResult::Ok));
if let Err(err) = metrics_server_test(
kubernetes
@@ -1447,19 +1411,21 @@ pub fn cluster_test(
upgrade_to_version.clone(),
localisation.clone(),
aws_zones,
cp.as_ref(),
&dns_provider,
cp,
dns_provider,
vpc_network_mode.clone(),
logger.as_ref(),
logger.clone(),
);
let mut upgrade_tx = engine.session().unwrap().transaction();
let mut delete_tx = engine.session().unwrap().transaction();
let mut upgrade_tx =
Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
let mut delete_tx =
Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
// Upgrade
if let Err(err) = upgrade_tx.create_kubernetes(upgraded_kubernetes.as_ref()) {
if let Err(err) = upgrade_tx.create_kubernetes() {
panic!("{:?}", err)
}
assert!(matches!(upgrade_tx.commit(logger.clone()), TransactionResult::Ok));
assert!(matches!(upgrade_tx.commit(), TransactionResult::Ok));
if let Err(err) = metrics_server_test(
upgraded_kubernetes
@@ -1475,10 +1441,10 @@ pub fn cluster_test(
}
// Delete
if let Err(err) = delete_tx.delete_kubernetes(upgraded_kubernetes.as_ref()) {
if let Err(err) = delete_tx.delete_kubernetes() {
panic!("{:?}", err)
}
assert!(matches!(delete_tx.commit(logger.clone()), TransactionResult::Ok));
assert!(matches!(delete_tx.commit(), TransactionResult::Ok));
return test_name.to_string();
}
@@ -1486,20 +1452,21 @@ pub fn cluster_test(
// Destroy env if any
if let Some(env) = environment_to_deploy {
let mut destroy_env_tx = engine.session().unwrap().transaction();
let mut destroy_env_tx =
Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
// Deploy env
if let Err(err) = destroy_env_tx.delete_environment(kubernetes.as_ref(), env) {
if let Err(err) = destroy_env_tx.delete_environment(env) {
panic!("{:?}", err)
}
assert!(matches!(destroy_env_tx.commit(logger.clone()), TransactionResult::Ok));
assert!(matches!(destroy_env_tx.commit(), TransactionResult::Ok));
}
// Delete
if let Err(err) = delete_tx.delete_kubernetes(kubernetes.as_ref()) {
if let Err(err) = delete_tx.delete_kubernetes() {
panic!("{:?}", err)
}
assert!(matches!(delete_tx.commit(logger.clone()), TransactionResult::Ok));
assert!(matches!(delete_tx.commit(), TransactionResult::Ok));
test_name.to_string()
}

View File

@@ -4,17 +4,20 @@ use qovery_engine::cloud_provider::digitalocean::kubernetes::DoksOptions;
use qovery_engine::cloud_provider::digitalocean::network::vpc::VpcInitKind;
use qovery_engine::cloud_provider::digitalocean::DO;
use qovery_engine::cloud_provider::models::NodeGroups;
use qovery_engine::cloud_provider::TerraformStateCredentials;
use qovery_engine::cloud_provider::{CloudProvider, TerraformStateCredentials};
use qovery_engine::container_registry::docr::DOCR;
use qovery_engine::engine::Engine;
use qovery_engine::engine::EngineConfig;
use qovery_engine::error::EngineError;
use qovery_engine::models::{Context, Environment};
use std::sync::Arc;
use crate::cloudflare::dns_provider_cloudflare;
use crate::common::{Cluster, ClusterDomain};
use crate::common::{get_environment_test_kubernetes, Cluster, ClusterDomain};
use crate::utilities::{build_platform_local_docker, FuncTestsSecrets};
use qovery_engine::cloud_provider::digitalocean::application::DoRegion;
use qovery_engine::cloud_provider::qovery::EngineLocation;
use qovery_engine::cloud_provider::Kind::Do;
use qovery_engine::dns_provider::DnsProvider;
use qovery_engine::logger::Logger;
pub const DO_KUBERNETES_MAJOR_VERSION: u8 = 1;
@@ -39,24 +42,32 @@ pub fn container_registry_digital_ocean(context: &Context) -> DOCR {
}
impl Cluster<DO, DoksOptions> for DO {
fn docker_cr_engine(context: &Context, logger: Box<dyn Logger>) -> Engine {
fn docker_cr_engine(context: &Context, logger: Box<dyn Logger>) -> EngineConfig {
// use DigitalOcean Container Registry
let container_registry = Box::new(container_registry_digital_ocean(context));
// use LocalDocker
let build_platform = Box::new(build_platform_local_docker(context, logger.clone()));
// use Digital Ocean
let cloud_provider = DO::cloud_provider(context);
let cloud_provider: Arc<Box<dyn CloudProvider>> = Arc::new(Self::cloud_provider(context));
let dns_provider: Arc<Box<dyn DnsProvider>> =
Arc::new(dns_provider_cloudflare(context, ClusterDomain::Default));
let dns_provider = Box::new(dns_provider_cloudflare(&context, ClusterDomain::Default));
let k = get_environment_test_kubernetes(
Do,
context,
cloud_provider.clone(),
dns_provider.clone(),
logger.clone(),
);
Engine::new(
EngineConfig::new(
context.clone(),
build_platform,
container_registry,
cloud_provider,
dns_provider,
logger,
Box::new(|| false),
k,
)
}

View File

@@ -3,19 +3,22 @@ use qovery_engine::build_platform::Image;
use qovery_engine::cloud_provider::scaleway::application::ScwZone;
use qovery_engine::cloud_provider::scaleway::kubernetes::KapsuleOptions;
use qovery_engine::cloud_provider::scaleway::Scaleway;
use qovery_engine::cloud_provider::TerraformStateCredentials;
use qovery_engine::cloud_provider::{CloudProvider, TerraformStateCredentials};
use qovery_engine::container_registry::scaleway_container_registry::ScalewayCR;
use qovery_engine::engine::Engine;
use qovery_engine::engine::EngineConfig;
use qovery_engine::error::EngineError;
use qovery_engine::models::{Context, Environment};
use qovery_engine::object_storage::scaleway_object_storage::{BucketDeleteStrategy, ScalewayOS};
use std::sync::Arc;
use crate::cloudflare::dns_provider_cloudflare;
use crate::utilities::{build_platform_local_docker, generate_id, FuncTestsSecrets};
use crate::common::{Cluster, ClusterDomain};
use crate::common::{get_environment_test_kubernetes, Cluster, ClusterDomain};
use qovery_engine::cloud_provider::models::NodeGroups;
use qovery_engine::cloud_provider::qovery::EngineLocation;
use qovery_engine::cloud_provider::Kind::Scw;
use qovery_engine::dns_provider::DnsProvider;
use qovery_engine::logger::Logger;
use tracing::error;
@@ -58,7 +61,7 @@ pub fn container_registry_scw(context: &Context) -> ScalewayCR {
}
impl Cluster<Scaleway, KapsuleOptions> for Scaleway {
fn docker_cr_engine(context: &Context, logger: Box<dyn Logger>) -> Engine {
fn docker_cr_engine(context: &Context, logger: Box<dyn Logger>) -> EngineConfig {
// use Scaleway CR
let container_registry = Box::new(container_registry_scw(context));
@@ -66,18 +69,25 @@ impl Cluster<Scaleway, KapsuleOptions> for Scaleway {
let build_platform = Box::new(build_platform_local_docker(context, logger.clone()));
// use Scaleway
let cloud_provider = Scaleway::cloud_provider(context);
let cloud_provider: Arc<Box<dyn CloudProvider>> = Arc::new(Self::cloud_provider(context));
let dns_provider: Arc<Box<dyn DnsProvider>> =
Arc::new(dns_provider_cloudflare(context, ClusterDomain::Default));
let dns_provider = Box::new(dns_provider_cloudflare(context, ClusterDomain::Default));
let cluster = get_environment_test_kubernetes(
Scw,
context,
cloud_provider.clone(),
dns_provider.clone(),
logger.clone(),
);
Engine::new(
EngineConfig::new(
context.clone(),
build_platform,
container_registry,
cloud_provider,
dns_provider,
logger,
Box::new(|| false),
cluster,
)
}

View File

@@ -5,7 +5,6 @@ use qovery_engine::cloud_provider::Kind;
use qovery_engine::models::{
Action, Clone2, Context, Database, DatabaseKind, DatabaseMode, Environment, EnvironmentAction, Port, Protocol,
};
use qovery_engine::transaction::TransactionResult;
use tracing::{span, Level};
use self::test_utilities::aws::{AWS_DATABASE_DISK_TYPE, AWS_DATABASE_INSTANCE_TYPE};
@@ -13,6 +12,7 @@ use self::test_utilities::utilities::{
context, engine_run_test, generate_id, get_pods, get_svc_name, init, is_pod_restarted_env, logger, FuncTestsSecrets,
};
use qovery_engine::models::DatabaseMode::{CONTAINER, MANAGED};
use qovery_engine::transaction::TransactionResult;
use test_utilities::common::{test_db, Infrastructure};
/**

View File

@@ -1,14 +1,12 @@
extern crate test_utilities;
use self::test_utilities::cloudflare::dns_provider_cloudflare;
use self::test_utilities::utilities::{context, engine_run_test, init, logger, FuncTestsSecrets};
use ::function_name::named;
use qovery_engine::cloud_provider::digitalocean::DO;
use tracing::{span, Level};
use self::test_utilities::common::{Cluster, ClusterDomain};
use qovery_engine::cloud_provider::digitalocean::kubernetes::DOKS;
use qovery_engine::transaction::TransactionResult;
use self::test_utilities::common::Cluster;
use qovery_engine::transaction::{Transaction, TransactionResult};
// Warning: This test shouldn't be ran by CI
// Note: this test creates the test cluster where all application tests will be ran
@@ -35,38 +33,17 @@ fn create_digitalocean_kubernetes_doks_test_cluster() {
.DIGITAL_OCEAN_TEST_CLUSTER_ID
.as_ref()
.expect("DIGITAL_OCEAN_TEST_CLUSTER_ID is not set");
let cluster_name = format!("qovery-{}", cluster_id.clone());
let logger = logger();
let context = context(organization_id.as_str(), cluster_id.as_str());
let engine = DO::docker_cr_engine(&context, logger.clone());
let session = engine.session().unwrap();
let mut tx = session.transaction();
let do_cluster = DO::cloud_provider(&context);
let nodes = DO::kubernetes_nodes();
let cloudflare = dns_provider_cloudflare(&context, ClusterDomain::Default);
let kubernetes = DOKS::new(
context.clone(),
cluster_id.to_string(),
uuid::Uuid::new_v4(),
cluster_name.to_string(),
test_utilities::digitalocean::DO_KUBERNETES_VERSION.to_string(),
test_utilities::digitalocean::DO_TEST_REGION,
do_cluster.as_ref(),
&cloudflare,
nodes,
DO::kubernetes_cluster_options(secrets, Option::from(cluster_name.to_string())),
logger.as_ref(),
)
.unwrap();
let mut tx = Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
// Deploy
if let Err(err) = tx.create_kubernetes(&kubernetes) {
if let Err(err) = tx.create_kubernetes() {
panic!("{:?}", err)
}
let ret = tx.commit(logger.clone());
let ret = tx.commit();
assert!(matches!(ret, TransactionResult::Ok));
test_name.to_string()
@@ -98,38 +75,17 @@ fn destroy_digitalocean_kubernetes_doks_test_cluster() {
.DIGITAL_OCEAN_TEST_CLUSTER_ID
.as_ref()
.expect("DIGITAL_OCEAN_TEST_CLUSTER_ID is not set");
let cluster_name = format!("qovery-{}", cluster_id.clone());
let logger = logger();
let context = context(organization_id.as_str(), cluster_id.as_str());
let engine = DO::docker_cr_engine(&context, logger.clone());
let session = engine.session().unwrap();
let mut tx = session.transaction();
let do_cluster = DO::cloud_provider(&context);
let nodes = DO::kubernetes_nodes();
let cloudflare = dns_provider_cloudflare(&context, ClusterDomain::Default);
let kubernetes = DOKS::new(
context.clone(),
cluster_id.to_string(),
uuid::Uuid::new_v4(),
cluster_name.to_string(),
test_utilities::digitalocean::DO_KUBERNETES_VERSION.to_string(),
test_utilities::digitalocean::DO_TEST_REGION,
do_cluster.as_ref(),
&cloudflare,
nodes,
DO::kubernetes_cluster_options(secrets, Option::from(cluster_name.to_string())),
logger.as_ref(),
)
.unwrap();
let mut tx = Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
// Destroy
if let Err(err) = tx.delete_kubernetes(&kubernetes) {
if let Err(err) = tx.delete_kubernetes() {
panic!("{:?}", err)
}
let ret = tx.commit(logger.clone());
let ret = tx.commit();
assert!(matches!(ret, TransactionResult::Ok));
test_name.to_string()

View File

@@ -1,14 +1,12 @@
extern crate test_utilities;
use self::test_utilities::cloudflare::dns_provider_cloudflare;
use self::test_utilities::utilities::{context, engine_run_test, init, logger, FuncTestsSecrets};
use ::function_name::named;
use tracing::{span, Level};
use self::test_utilities::common::{Cluster, ClusterDomain};
use qovery_engine::cloud_provider::scaleway::kubernetes::Kapsule;
use self::test_utilities::common::Cluster;
use qovery_engine::cloud_provider::scaleway::Scaleway;
use qovery_engine::transaction::TransactionResult;
use qovery_engine::transaction::{Transaction, TransactionResult};
// Warning: This test shouldn't be ran by CI
// Note: this test creates the test cluster where all application tests will be ran
@@ -39,34 +37,14 @@ fn create_scaleway_kubernetes_kapsule_test_cluster() {
let logger = logger();
let context = context(organization_id.as_str(), cluster_id.as_str());
let engine = Scaleway::docker_cr_engine(&context, logger.clone());
let session = engine.session().unwrap();
let mut tx = session.transaction();
let scw_cluster = Scaleway::cloud_provider(&context);
let nodes = Scaleway::kubernetes_nodes();
let cloudflare = dns_provider_cloudflare(&context, ClusterDomain::Default);
let kubernetes = Kapsule::new(
context.clone(),
cluster_id.to_string(),
uuid::Uuid::new_v4(),
format!("qovery-{}", cluster_id.to_string()),
test_utilities::scaleway::SCW_KUBERNETES_VERSION.to_string(),
test_utilities::scaleway::SCW_TEST_ZONE,
scw_cluster.as_ref(),
&cloudflare,
nodes,
Scaleway::kubernetes_cluster_options(secrets, None),
logger.as_ref(),
)
.unwrap();
let mut tx = Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
// Deploy
if let Err(err) = tx.create_kubernetes(&kubernetes) {
if let Err(err) = tx.create_kubernetes() {
panic!("{:?}", err)
}
assert!(matches!(tx.commit(logger.clone()), TransactionResult::Ok));
assert!(matches!(tx.commit(), TransactionResult::Ok));
test_name.to_string()
});
@@ -101,33 +79,13 @@ fn destroy_scaleway_kubernetes_kapsule_test_cluster() {
let logger = logger();
let context = context(organization_id.as_str(), cluster_id.as_str());
let engine = Scaleway::docker_cr_engine(&context, logger.clone());
let session = engine.session().unwrap();
let mut tx = session.transaction();
let scw_cluster = Scaleway::cloud_provider(&context);
let nodes = Scaleway::kubernetes_nodes();
let cloudflare = dns_provider_cloudflare(&context, ClusterDomain::Default);
let kubernetes = Kapsule::new(
context.clone(),
cluster_id.to_string(),
uuid::Uuid::new_v4(),
format!("qovery-{}", cluster_id.to_string()),
test_utilities::scaleway::SCW_KUBERNETES_VERSION.to_string(),
test_utilities::scaleway::SCW_TEST_ZONE,
scw_cluster.as_ref(),
&cloudflare,
nodes,
Scaleway::kubernetes_cluster_options(secrets, None),
logger.as_ref(),
)
.unwrap();
let mut tx = Transaction::new(&engine, logger.clone(), Box::new(|| false), Box::new(|_| {})).unwrap();
// Destroy
if let Err(err) = tx.delete_kubernetes(&kubernetes) {
if let Err(err) = tx.delete_kubernetes() {
panic!("{:?}", err)
}
let ret = tx.commit(logger.clone());
let ret = tx.commit();
assert!(matches!(ret, TransactionResult::Ok));
test_name.to_string()