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 derive_new::new;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::topology::Topology;
|
||||
use crate::{executors::ExecutorError, topology::Topology};
|
||||
|
||||
/// An ApplicationFeature provided by harmony, such as Backups, Monitoring, MultisiteAvailability,
|
||||
/// ContinuousIntegration, ContinuousDelivery
|
||||
@ -9,7 +12,10 @@ use crate::topology::Topology;
|
||||
pub trait ApplicationFeature<T: Topology>:
|
||||
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;
|
||||
}
|
||||
|
||||
@ -40,3 +46,60 @@ impl<T: Topology> Clone for Box<dyn ApplicationFeature<T>> {
|
||||
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,
|
||||
inventory::Inventory,
|
||||
modules::application::{
|
||||
ApplicationFeature, HelmPackage, OCICompliant,
|
||||
ApplicationFeature, HelmPackage, InstallationError, InstallationOutcome, OCICompliant,
|
||||
features::{ArgoApplication, ArgoHelmScore},
|
||||
},
|
||||
score::Score,
|
||||
@ -141,7 +141,10 @@ impl<
|
||||
T: Topology + HelmCommand + MultiTargetTopology + K8sclient + Ingress + 'static,
|
||||
> 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 domain = topology
|
||||
.get_domain(&self.application.name())
|
||||
@ -205,7 +208,11 @@ impl<
|
||||
.unwrap();
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
|
||||
Ok(InstallationOutcome::success_with_details(vec![format!(
|
||||
"{}: {domain}",
|
||||
self.application.name()
|
||||
)]))
|
||||
}
|
||||
fn name(&self) -> String {
|
||||
"ContinuousDelivery".to_string()
|
||||
|
@ -2,7 +2,7 @@ use async_trait::async_trait;
|
||||
use log::info;
|
||||
|
||||
use crate::{
|
||||
modules::application::ApplicationFeature,
|
||||
modules::application::{ApplicationFeature, InstallationError, InstallationOutcome},
|
||||
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
|
||||
#[async_trait]
|
||||
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!(
|
||||
"Making sure public endpoint is installed for 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::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus;
|
||||
use crate::topology::MultiTargetTopology;
|
||||
@ -43,7 +45,10 @@ impl<
|
||||
+ std::fmt::Debug,
|
||||
> 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");
|
||||
let namespace = topology
|
||||
.get_tenant_config()
|
||||
@ -103,7 +108,7 @@ impl<
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
Ok(InstallationOutcome::success())
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
|
@ -1,6 +1,8 @@
|
||||
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::rhobs_application_monitoring_score::ApplicationRHOBMonitoringScore;
|
||||
|
||||
@ -43,7 +45,10 @@ impl<
|
||||
+ PrometheusApplicationMonitoring<RHOBObservability>,
|
||||
> 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");
|
||||
let namespace = topology
|
||||
.get_tenant_config()
|
||||
@ -106,7 +111,7 @@ impl<
|
||||
.interpret(&Inventory::empty(), topology)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
Ok(InstallationOutcome::success())
|
||||
}
|
||||
fn name(&self) -> String {
|
||||
"Monitoring".to_string()
|
||||
|
@ -24,8 +24,8 @@ use harmony_types::id::Id;
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ApplicationFeatureStatus {
|
||||
Installing,
|
||||
Installed,
|
||||
Failed { details: String },
|
||||
Installed { details: Vec<String> },
|
||||
Failed { message: String },
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
let _ = match feature.ensure_installed(topology).await {
|
||||
Ok(()) => {
|
||||
Ok(outcome) => {
|
||||
instrumentation::instrument(HarmonyEvent::ApplicationFeatureStateChanged {
|
||||
topology: topology.name().into(),
|
||||
application: self.application.name(),
|
||||
feature: feature.name(),
|
||||
status: ApplicationFeatureStatus::Installed,
|
||||
status: ApplicationFeatureStatus::Installed {
|
||||
details: match outcome {
|
||||
InstallationOutcome::Success { details } => details,
|
||||
InstallationOutcome::Noop => vec![],
|
||||
},
|
||||
},
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
Err(msg) => {
|
||||
Err(error) => {
|
||||
instrumentation::instrument(HarmonyEvent::ApplicationFeatureStateChanged {
|
||||
topology: topology.name().into(),
|
||||
application: self.application.name(),
|
||||
feature: feature.name(),
|
||||
status: ApplicationFeatureStatus::Failed {
|
||||
details: msg.clone(),
|
||||
message: error.to_string(),
|
||||
},
|
||||
})
|
||||
.unwrap();
|
||||
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 => {
|
||||
info!("Installing feature '{feature}' for '{application}'...");
|
||||
}
|
||||
ApplicationFeatureStatus::Installed => {
|
||||
ApplicationFeatureStatus::Installed { details: _ } => {
|
||||
info!(status = "finished"; "Feature '{feature}' installed");
|
||||
}
|
||||
ApplicationFeatureStatus::Failed { details } => {
|
||||
ApplicationFeatureStatus::Failed { message: details } => {
|
||||
error!(status = "failed"; "Feature '{feature}' installation failed: {details}");
|
||||
}
|
||||
},
|
||||
|
@ -1,6 +1,9 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use harmony::instrumentation::{self, HarmonyEvent};
|
||||
use harmony::{
|
||||
instrumentation::{self, HarmonyEvent},
|
||||
modules::application::ApplicationFeatureStatus,
|
||||
};
|
||||
|
||||
use crate::theme;
|
||||
|
||||
@ -11,26 +14,43 @@ pub fn init() {
|
||||
move |event| {
|
||||
let mut details = details.lock().unwrap();
|
||||
|
||||
if let HarmonyEvent::InterpretExecutionFinished {
|
||||
match event {
|
||||
HarmonyEvent::InterpretExecutionFinished {
|
||||
execution_id: _,
|
||||
topology: _,
|
||||
interpret: _,
|
||||
score: _,
|
||||
outcome: Ok(outcome),
|
||||
} = event
|
||||
{
|
||||
} => {
|
||||
if outcome.status == harmony::interpret::InterpretStatus::SUCCESS {
|
||||
details.extend(outcome.details.clone());
|
||||
}
|
||||
} else if let HarmonyEvent::HarmonyFinished = event
|
||||
&& !details.is_empty()
|
||||
{
|
||||
println!("\n{} All done! What's next for you:", theme::EMOJI_SUMMARY);
|
||||
}
|
||||
HarmonyEvent::ApplicationFeatureStateChanged {
|
||||
topology: _,
|
||||
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() {
|
||||
println!("- {detail}");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user