fix: unjank the demo #85
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,7 +2,4 @@ target
|
||||
private_repos
|
||||
log/
|
||||
*.tgz
|
||||
examples/rust/examples/rust/webapp/helm/
|
||||
examples/rust/examples/rust/webapp/Dockerfile.harmony
|
||||
examples/rust/webapp/helm/harmony-example-rust-webapp-chart/
|
||||
.gitignore
|
||||
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1739,6 +1739,7 @@ name = "harmony"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"bollard",
|
||||
"chrono",
|
||||
"cidr",
|
||||
|
@ -44,12 +44,6 @@ async fn main() {
|
||||
};
|
||||
|
||||
let topology = K8sAnywhereTopology::from_env();
|
||||
|
||||
// topology
|
||||
// .provision_tenant(&tenant.config)
|
||||
// .await
|
||||
// .expect("couldn't provision tenant");
|
||||
|
||||
let mut maestro = Maestro::initialize(Inventory::autoload(), topology)
|
||||
.await
|
||||
.unwrap();
|
||||
@ -61,59 +55,16 @@ async fn main() {
|
||||
framework: Some(RustWebFramework::Leptos),
|
||||
});
|
||||
|
||||
let ntfy = NtfyScore {
|
||||
namespace: tenant.clone().config.name,
|
||||
};
|
||||
|
||||
let ntfy_default_auth_username = "harmony";
|
||||
let ntfy_default_auth_password = "harmony";
|
||||
let ntfy_default_auth_header = format!(
|
||||
"Basic {}",
|
||||
general_purpose::STANDARD.encode(format!(
|
||||
"{ntfy_default_auth_username}:{ntfy_default_auth_password}"
|
||||
))
|
||||
);
|
||||
|
||||
let ntfy_default_auth_param = general_purpose::STANDARD
|
||||
.encode(ntfy_default_auth_header)
|
||||
.rsplit("=")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
|
||||
let ntfy_receiver = WebhookReceiver {
|
||||
name: "ntfy-webhook".to_string(),
|
||||
url: Url::Url(
|
||||
url::Url::parse(
|
||||
format!(
|
||||
"http://ntfy.{}.svc.cluster.local/rust-web-app?auth={ntfy_default_auth_param}",
|
||||
tenant.clone().config.name
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
};
|
||||
|
||||
let alerting_score = HelmPrometheusAlertingScore {
|
||||
receivers: vec![Box::new(ntfy_receiver)],
|
||||
rules: vec![],
|
||||
service_monitors: vec![],
|
||||
};
|
||||
|
||||
let app = ApplicationScore {
|
||||
features: vec![
|
||||
Box::new(ContinuousDelivery {
|
||||
application: application.clone(),
|
||||
}), // TODO add monitoring, backups, multisite ha, etc
|
||||
Box::new(Monitoring {}),
|
||||
],
|
||||
johnride
commented
Our users have no clue what ntfy or prometheus is, this is leaking Harmony's internals. Alerting is just a feature of the infrastructure that they do not need to know how it is implemented. Our users have no clue what ntfy or prometheus is, this is leaking Harmony's internals. Alerting is just a feature of the infrastructure that they do not need to know how it is implemented.
|
||||
application,
|
||||
};
|
||||
|
||||
maestro.register_all(vec![
|
||||
Box::new(tenant),
|
||||
Box::new(ntfy),
|
||||
Box::new(alerting_score),
|
||||
Box::new(app),
|
||||
]);
|
||||
maestro.register_all(vec![Box::new(app)]);
|
||||
harmony_cli::init(maestro, None).await.unwrap();
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ tempfile = "3.20.0"
|
||||
serde_with = "3.14.0"
|
||||
bollard.workspace = true
|
||||
tar.workspace = true
|
||||
base64.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions.workspace = true
|
||||
|
@ -204,14 +204,7 @@ impl<
|
||||
.unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
todo!("1. Create ArgoCD score that installs argo using helm chart, see if Taha's already done it
|
||||
- [X] Package app (docker image, helm chart)
|
||||
- [X] Push to registry
|
||||
- [X] Push only if staging or prod
|
||||
- [X] Deploy to local k3d when target is local
|
||||
- [ ] Poke Argo
|
||||
- [ ] Ensure app is up")
|
||||
Ok(())
|
||||
}
|
||||
fn name(&self) -> String {
|
||||
"ContinuousDelivery".to_string()
|
||||
|
@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use log::info;
|
||||
|
||||
use crate::{
|
||||
@ -6,28 +7,74 @@ use crate::{
|
||||
modules::{
|
||||
application::ApplicationFeature,
|
||||
monitoring::{
|
||||
alert_channel::webhook_receiver::WebhookReceiver,
|
||||
application_monitoring::k8s_application_monitoring_score::ApplicationPrometheusMonitoringScore,
|
||||
kube_prometheus::types::{NamespaceSelector, ServiceMonitor},
|
||||
ntfy::ntfy::NtfyScore,
|
||||
},
|
||||
},
|
||||
score::Score,
|
||||
topology::{HelmCommand, Topology, tenant::TenantManager},
|
||||
topology::{HelmCommand, K8sclient, Topology, Url, tenant::TenantManager},
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Monitoring {}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology + HelmCommand + 'static + TenantManager> ApplicationFeature<T> for Monitoring {
|
||||
impl<T: Topology + HelmCommand + K8sclient + 'static + TenantManager> ApplicationFeature<T>
|
||||
taha marked this conversation as resolved
Outdated
wjro
commented
if we add pub application: Arc to monitoring we can use the application name as namespace so that it is consistent with the way continuous _delivery feature gets the namespace if we add pub application: Arc<dyn Application> to monitoring we can use the application name as namespace so that it is consistent with the way continuous _delivery feature gets the namespace
|
||||
for Monitoring
|
||||
{
|
||||
async fn ensure_installed(&self, topology: &T) -> Result<(), String> {
|
||||
info!("Ensuring monitoring is available for application");
|
||||
|
||||
let ntfy = NtfyScore {
|
||||
namespace: topology
|
||||
.get_tenant_config()
|
||||
.await
|
||||
.expect("couldn't get tenant config")
|
||||
.name,
|
||||
};
|
||||
ntfy.create_interpret()
|
||||
.execute(&Inventory::empty(), topology)
|
||||
.await
|
||||
taha marked this conversation as resolved
wjro
commented
rather than hardcoding namespace here rather than hardcoding namespace here
|
||||
.expect("couldn't create interpret for ntfy");
|
||||
|
||||
let ntfy_default_auth_username = "harmony";
|
||||
let ntfy_default_auth_password = "harmony";
|
||||
let ntfy_default_auth_header = format!(
|
||||
"Basic {}",
|
||||
general_purpose::STANDARD.encode(format!(
|
||||
"{ntfy_default_auth_username}:{ntfy_default_auth_password}"
|
||||
))
|
||||
);
|
||||
|
||||
let ntfy_default_auth_param = general_purpose::STANDARD
|
||||
.encode(ntfy_default_auth_header)
|
||||
.rsplit("=")
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.to_string();
|
||||
|
||||
let ntfy_receiver = WebhookReceiver {
|
||||
name: "ntfy-webhook".to_string(),
|
||||
url: Url::Url(
|
||||
url::Url::parse(
|
||||
format!(
|
||||
"http://ntfy.{}.svc.cluster.local/rust-web-app?auth={ntfy_default_auth_param}",
|
||||
topology.get_tenant_config().await.expect("couldn't get tenant config").name
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
};
|
||||
|
||||
let mut service_monitor = ServiceMonitor::default();
|
||||
service_monitor.namespace_selector = Some(NamespaceSelector {
|
||||
any: true,
|
||||
match_names: vec![],
|
||||
});
|
||||
let alerting_score = ApplicationPrometheusMonitoringScore {
|
||||
receivers: vec![],
|
||||
receivers: vec![Box::new(ntfy_receiver)],
|
||||
rules: vec![],
|
||||
service_monitors: vec![service_monitor],
|
||||
};
|
||||
|
@ -59,9 +59,7 @@ impl<A: Application, T: Topology + std::fmt::Debug> Interpret<T> for Application
|
||||
}
|
||||
};
|
||||
}
|
||||
todo!(
|
||||
"Do I need to do anything more than this here?? I feel like the Application trait itself should expose something like ensure_ready but its becoming redundant. We'll see as this evolves."
|
||||
)
|
||||
Ok(Outcome::success("successfully created app".to_string()))
|
||||
}
|
||||
|
||||
fn get_name(&self) -> InterpretName {
|
||||
|
@ -360,7 +360,11 @@ impl RustWebapp {
|
||||
image_url: &str,
|
||||
) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
let chart_name = format!("{}-chart", self.name);
|
||||
let chart_dir = self.project_root.join("helm").join(&chart_name);
|
||||
let chart_dir = self
|
||||
.project_root
|
||||
.join(".harmony_generated")
|
||||
.join("helm")
|
||||
.join(&chart_name);
|
||||
let templates_dir = chart_dir.join("templates");
|
||||
fs::create_dir_all(&templates_dir)?;
|
||||
|
||||
@ -537,11 +541,15 @@ spec:
|
||||
info!(
|
||||
"Launching `helm package {}` cli with CWD {}",
|
||||
chart_dirname.to_string_lossy(),
|
||||
&self.project_root.join("helm").to_string_lossy()
|
||||
&self
|
||||
.project_root
|
||||
.join(".harmony_generated")
|
||||
.join("helm")
|
||||
.to_string_lossy()
|
||||
);
|
||||
let output = process::Command::new("helm")
|
||||
.args(["package", chart_dirname.to_str().unwrap()])
|
||||
.current_dir(&self.project_root.join("helm")) // Run package from the parent dir
|
||||
.current_dir(&self.project_root.join(".harmony_generated").join("helm")) // Run package from the parent dir
|
||||
.output()?;
|
||||
|
||||
self.check_output(&output, "Failed to package Helm chart")?;
|
||||
@ -558,7 +566,11 @@ spec:
|
||||
}
|
||||
|
||||
// The output from helm is relative, so we join it with the execution directory.
|
||||
Ok(self.project_root.join("helm").join(tgz_name))
|
||||
Ok(self
|
||||
.project_root
|
||||
.join(".harmony_generated")
|
||||
.join("helm")
|
||||
.join(tgz_name))
|
||||
}
|
||||
|
||||
/// Pushes a packaged Helm chart to an OCI registry.
|
||||
|
Loading…
Reference in New Issue
Block a user
These would be better in the examples's own gitignore file
examples/rust/.gitignore
. Eventually we may have hundreds of examples, we don't want thousands of lines in this file.But thinking a bit further, this points towards a problem on the way we manage these generated files. They should probably end up in a harmony specific directory that we can then ignore in a single line for all examples. Maybe
harmony_generated
orharmony_build
?