report application deploy URL
This commit is contained in:
parent
f3639c604c
commit
7bc083701e
@ -1,7 +1,10 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use derive_new::new;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::topology::Topology;
|
use crate::{executors::ExecutorError, topology::Topology};
|
||||||
|
|
||||||
/// An ApplicationFeature provided by harmony, such as Backups, Monitoring, MultisiteAvailability,
|
/// An ApplicationFeature provided by harmony, such as Backups, Monitoring, MultisiteAvailability,
|
||||||
/// ContinuousIntegration, ContinuousDelivery
|
/// ContinuousIntegration, ContinuousDelivery
|
||||||
@ -9,7 +12,10 @@ use crate::topology::Topology;
|
|||||||
pub trait ApplicationFeature<T: Topology>:
|
pub trait ApplicationFeature<T: Topology>:
|
||||||
std::fmt::Debug + Send + Sync + ApplicationFeatureClone<T>
|
std::fmt::Debug + Send + Sync + ApplicationFeatureClone<T>
|
||||||
{
|
{
|
||||||
async fn ensure_installed(&self, topology: &T) -> Result<(), String>;
|
async fn ensure_installed(
|
||||||
|
&self,
|
||||||
|
topology: &T,
|
||||||
|
) -> Result<InstallationOutcome, InstallationError>;
|
||||||
fn name(&self) -> String;
|
fn name(&self) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,3 +46,60 @@ impl<T: Topology> Clone for Box<dyn ApplicationFeature<T>> {
|
|||||||
self.clone_box()
|
self.clone_box()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum InstallationOutcome {
|
||||||
|
Success { details: Vec<String> },
|
||||||
|
Noop,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstallationOutcome {
|
||||||
|
pub fn success() -> Self {
|
||||||
|
Self::Success { details: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn success_with_details(details: Vec<String>) -> Self {
|
||||||
|
Self::Success { details }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn noop() -> Self {
|
||||||
|
Self::Noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, new)]
|
||||||
|
pub struct InstallationError {
|
||||||
|
msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for InstallationError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(&self.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for InstallationError {}
|
||||||
|
|
||||||
|
impl From<ExecutorError> for InstallationError {
|
||||||
|
fn from(value: ExecutorError) -> Self {
|
||||||
|
Self {
|
||||||
|
msg: format!("InstallationError : {value}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<kube::Error> for InstallationError {
|
||||||
|
fn from(value: kube::Error) -> Self {
|
||||||
|
Self {
|
||||||
|
msg: format!("InstallationError : {value}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for InstallationError {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self {
|
||||||
|
msg: format!("PreparationError : {value}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,7 +10,7 @@ use crate::{
|
|||||||
data::Version,
|
data::Version,
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
modules::application::{
|
modules::application::{
|
||||||
ApplicationFeature, HelmPackage, OCICompliant,
|
ApplicationFeature, HelmPackage, InstallationError, InstallationOutcome, OCICompliant,
|
||||||
features::{ArgoApplication, ArgoHelmScore},
|
features::{ArgoApplication, ArgoHelmScore},
|
||||||
},
|
},
|
||||||
score::Score,
|
score::Score,
|
||||||
@ -141,7 +141,10 @@ impl<
|
|||||||
T: Topology + HelmCommand + MultiTargetTopology + K8sclient + Ingress + 'static,
|
T: Topology + HelmCommand + MultiTargetTopology + K8sclient + Ingress + 'static,
|
||||||
> ApplicationFeature<T> for ContinuousDelivery<A>
|
> ApplicationFeature<T> for ContinuousDelivery<A>
|
||||||
{
|
{
|
||||||
async fn ensure_installed(&self, topology: &T) -> Result<(), String> {
|
async fn ensure_installed(
|
||||||
|
&self,
|
||||||
|
topology: &T,
|
||||||
|
) -> Result<InstallationOutcome, InstallationError> {
|
||||||
let image = self.application.image_name();
|
let image = self.application.image_name();
|
||||||
let domain = topology
|
let domain = topology
|
||||||
.get_domain(&self.application.name())
|
.get_domain(&self.application.name())
|
||||||
@ -205,7 +208,11 @@ impl<
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
|
||||||
|
Ok(InstallationOutcome::success_with_details(vec![format!(
|
||||||
|
"{}: {domain}",
|
||||||
|
self.application.name()
|
||||||
|
)]))
|
||||||
}
|
}
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
"ContinuousDelivery".to_string()
|
"ContinuousDelivery".to_string()
|
||||||
|
@ -2,7 +2,7 @@ use async_trait::async_trait;
|
|||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
modules::application::ApplicationFeature,
|
modules::application::{ApplicationFeature, InstallationError, InstallationOutcome},
|
||||||
topology::{K8sclient, Topology},
|
topology::{K8sclient, Topology},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,7 +29,10 @@ impl Default for PublicEndpoint {
|
|||||||
/// For now we only suport K8s ingress, but we will support more stuff at some point
|
/// For now we only suport K8s ingress, but we will support more stuff at some point
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<T: Topology + K8sclient + 'static> ApplicationFeature<T> for PublicEndpoint {
|
impl<T: Topology + K8sclient + 'static> ApplicationFeature<T> for PublicEndpoint {
|
||||||
async fn ensure_installed(&self, _topology: &T) -> Result<(), String> {
|
async fn ensure_installed(
|
||||||
|
&self,
|
||||||
|
_topology: &T,
|
||||||
|
) -> Result<InstallationOutcome, InstallationError> {
|
||||||
info!(
|
info!(
|
||||||
"Making sure public endpoint is installed for port {}",
|
"Making sure public endpoint is installed for port {}",
|
||||||
self.application_port
|
self.application_port
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use crate::modules::application::{Application, ApplicationFeature};
|
use crate::modules::application::{
|
||||||
|
Application, ApplicationFeature, InstallationError, InstallationOutcome,
|
||||||
|
};
|
||||||
use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore;
|
use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore;
|
||||||
use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus;
|
use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus;
|
||||||
use crate::topology::MultiTargetTopology;
|
use crate::topology::MultiTargetTopology;
|
||||||
@ -43,7 +45,10 @@ impl<
|
|||||||
+ std::fmt::Debug,
|
+ std::fmt::Debug,
|
||||||
> ApplicationFeature<T> for Monitoring
|
> ApplicationFeature<T> for Monitoring
|
||||||
{
|
{
|
||||||
async fn ensure_installed(&self, topology: &T) -> Result<(), String> {
|
async fn ensure_installed(
|
||||||
|
&self,
|
||||||
|
topology: &T,
|
||||||
|
) -> Result<InstallationOutcome, InstallationError> {
|
||||||
info!("Ensuring monitoring is available for application");
|
info!("Ensuring monitoring is available for application");
|
||||||
let namespace = topology
|
let namespace = topology
|
||||||
.get_tenant_config()
|
.get_tenant_config()
|
||||||
@ -103,7 +108,7 @@ impl<
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(InstallationOutcome::success())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::modules::application::{Application, ApplicationFeature};
|
use crate::modules::application::{
|
||||||
|
Application, ApplicationFeature, InstallationError, InstallationOutcome,
|
||||||
|
};
|
||||||
use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore;
|
use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore;
|
||||||
use crate::modules::monitoring::application_monitoring::rhobs_application_monitoring_score::ApplicationRHOBMonitoringScore;
|
use crate::modules::monitoring::application_monitoring::rhobs_application_monitoring_score::ApplicationRHOBMonitoringScore;
|
||||||
|
|
||||||
@ -43,7 +45,10 @@ impl<
|
|||||||
+ PrometheusApplicationMonitoring<RHOBObservability>,
|
+ PrometheusApplicationMonitoring<RHOBObservability>,
|
||||||
> ApplicationFeature<T> for RHOBMonitoring
|
> ApplicationFeature<T> for RHOBMonitoring
|
||||||
{
|
{
|
||||||
async fn ensure_installed(&self, topology: &T) -> Result<(), String> {
|
async fn ensure_installed(
|
||||||
|
&self,
|
||||||
|
topology: &T,
|
||||||
|
) -> Result<InstallationOutcome, InstallationError> {
|
||||||
info!("Ensuring monitoring is available for application");
|
info!("Ensuring monitoring is available for application");
|
||||||
let namespace = topology
|
let namespace = topology
|
||||||
.get_tenant_config()
|
.get_tenant_config()
|
||||||
@ -106,7 +111,7 @@ impl<
|
|||||||
.interpret(&Inventory::empty(), topology)
|
.interpret(&Inventory::empty(), topology)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
Ok(())
|
Ok(InstallationOutcome::success())
|
||||||
}
|
}
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
"Monitoring".to_string()
|
"Monitoring".to_string()
|
||||||
|
@ -24,8 +24,8 @@ use harmony_types::id::Id;
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ApplicationFeatureStatus {
|
pub enum ApplicationFeatureStatus {
|
||||||
Installing,
|
Installing,
|
||||||
Installed,
|
Installed { details: Vec<String> },
|
||||||
Failed { details: String },
|
Failed { message: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Application: std::fmt::Debug + Send + Sync {
|
pub trait Application: std::fmt::Debug + Send + Sync {
|
||||||
@ -65,27 +65,32 @@ impl<A: Application, T: Topology + std::fmt::Debug> Interpret<T> for Application
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let _ = match feature.ensure_installed(topology).await {
|
let _ = match feature.ensure_installed(topology).await {
|
||||||
Ok(()) => {
|
Ok(outcome) => {
|
||||||
instrumentation::instrument(HarmonyEvent::ApplicationFeatureStateChanged {
|
instrumentation::instrument(HarmonyEvent::ApplicationFeatureStateChanged {
|
||||||
topology: topology.name().into(),
|
topology: topology.name().into(),
|
||||||
application: self.application.name(),
|
application: self.application.name(),
|
||||||
feature: feature.name(),
|
feature: feature.name(),
|
||||||
status: ApplicationFeatureStatus::Installed,
|
status: ApplicationFeatureStatus::Installed {
|
||||||
|
details: match outcome {
|
||||||
|
InstallationOutcome::Success { details } => details,
|
||||||
|
InstallationOutcome::Noop => vec![],
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
Err(msg) => {
|
Err(error) => {
|
||||||
instrumentation::instrument(HarmonyEvent::ApplicationFeatureStateChanged {
|
instrumentation::instrument(HarmonyEvent::ApplicationFeatureStateChanged {
|
||||||
topology: topology.name().into(),
|
topology: topology.name().into(),
|
||||||
application: self.application.name(),
|
application: self.application.name(),
|
||||||
feature: feature.name(),
|
feature: feature.name(),
|
||||||
status: ApplicationFeatureStatus::Failed {
|
status: ApplicationFeatureStatus::Failed {
|
||||||
details: msg.clone(),
|
message: error.to_string(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
return Err(InterpretError::new(format!(
|
return Err(InterpretError::new(format!(
|
||||||
"Application Interpret failed to install feature : {msg}"
|
"Application Interpret failed to install feature : {error}"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -178,10 +178,10 @@ fn handle_events() {
|
|||||||
ApplicationFeatureStatus::Installing => {
|
ApplicationFeatureStatus::Installing => {
|
||||||
info!("Installing feature '{feature}' for '{application}'...");
|
info!("Installing feature '{feature}' for '{application}'...");
|
||||||
}
|
}
|
||||||
ApplicationFeatureStatus::Installed => {
|
ApplicationFeatureStatus::Installed { details: _ } => {
|
||||||
info!(status = "finished"; "Feature '{feature}' installed");
|
info!(status = "finished"; "Feature '{feature}' installed");
|
||||||
}
|
}
|
||||||
ApplicationFeatureStatus::Failed { details } => {
|
ApplicationFeatureStatus::Failed { message: details } => {
|
||||||
error!(status = "failed"; "Feature '{feature}' installation failed: {details}");
|
error!(status = "failed"; "Feature '{feature}' installation failed: {details}");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use harmony::instrumentation::{self, HarmonyEvent};
|
use harmony::{
|
||||||
|
instrumentation::{self, HarmonyEvent},
|
||||||
|
modules::application::ApplicationFeatureStatus,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::theme;
|
use crate::theme;
|
||||||
|
|
||||||
@ -11,26 +14,43 @@ pub fn init() {
|
|||||||
move |event| {
|
move |event| {
|
||||||
let mut details = details.lock().unwrap();
|
let mut details = details.lock().unwrap();
|
||||||
|
|
||||||
if let HarmonyEvent::InterpretExecutionFinished {
|
match event {
|
||||||
|
HarmonyEvent::InterpretExecutionFinished {
|
||||||
execution_id: _,
|
execution_id: _,
|
||||||
topology: _,
|
topology: _,
|
||||||
interpret: _,
|
interpret: _,
|
||||||
score: _,
|
score: _,
|
||||||
outcome: Ok(outcome),
|
outcome: Ok(outcome),
|
||||||
} = event
|
} => {
|
||||||
{
|
|
||||||
if outcome.status == harmony::interpret::InterpretStatus::SUCCESS {
|
if outcome.status == harmony::interpret::InterpretStatus::SUCCESS {
|
||||||
details.extend(outcome.details.clone());
|
details.extend(outcome.details.clone());
|
||||||
}
|
}
|
||||||
} else if let HarmonyEvent::HarmonyFinished = event
|
}
|
||||||
&& !details.is_empty()
|
HarmonyEvent::ApplicationFeatureStateChanged {
|
||||||
{
|
topology: _,
|
||||||
println!("\n{} All done! What's next for you:", theme::EMOJI_SUMMARY);
|
application: _,
|
||||||
|
feature: _,
|
||||||
|
status:
|
||||||
|
ApplicationFeatureStatus::Installed {
|
||||||
|
details: feature_details,
|
||||||
|
},
|
||||||
|
} => {
|
||||||
|
details.extend(feature_details.clone());
|
||||||
|
}
|
||||||
|
HarmonyEvent::HarmonyFinished => {
|
||||||
|
if !details.is_empty() {
|
||||||
|
println!(
|
||||||
|
"\n{} All done! Here's what's next for you:",
|
||||||
|
theme::EMOJI_SUMMARY
|
||||||
|
);
|
||||||
for detail in details.iter() {
|
for detail in details.iter() {
|
||||||
println!("- {detail}");
|
println!("- {detail}");
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user