diff --git a/README.md b/README.md index e77718e..4ccdae7 100644 --- a/README.md +++ b/README.md @@ -36,48 +36,59 @@ These principles surface as simple, ergonomic Rust APIs that let teams focus on ## 2 · Quick Start -The snippet below spins up a complete **production-grade LAMP stack** with monitoring. Swap it for your own scores to deploy anything from microservices to machine-learning pipelines. +The snippet below spins up a complete **production-grade Rust + Leptos Webapp** with monitoring. Swap it for your own scores to deploy anything from microservices to machine-learning pipelines. ```rust use harmony::{ - data::Version, inventory::Inventory, - maestro::Maestro, modules::{ - lamp::{LAMPConfig, LAMPScore}, - monitoring::monitoring_alerting::MonitoringAlertingStackScore, + application::{ + ApplicationScore, RustWebFramework, RustWebapp, + features::{PackagingDeployment, rhob_monitoring::Monitoring}, + }, + monitoring::alert_channel::discord_alert_channel::DiscordWebhook, }, - topology::{K8sAnywhereTopology, Url}, + topology::K8sAnywhereTopology, }; +use harmony_macros::hurl; +use std::{path::PathBuf, sync::Arc}; #[tokio::main] async fn main() { - // 1. Describe what you want - let lamp_stack = LAMPScore { - name: "harmony-lamp-demo".into(), - domain: Url::Url(url::Url::parse("https://lampdemo.example.com").unwrap()), - php_version: Version::from("8.3.0").unwrap(), - config: LAMPConfig { - project_root: "./php".into(), - database_size: "4Gi".into(), - ..Default::default() - }, + let application = Arc::new(RustWebapp { + name: "harmony-example-leptos".to_string(), + project_root: PathBuf::from(".."), // <== Your project root, usually .. if you use the standard `/harmony` folder + framework: Some(RustWebFramework::Leptos), + service_port: 8080, + }); + + // Define your Application deployment and the features you want + let app = ApplicationScore { + features: vec![ + Box::new(PackagingDeployment { + application: application.clone(), + }), + Box::new(Monitoring { + application: application.clone(), + alert_receiver: vec![ + Box::new(DiscordWebhook { + name: "test-discord".to_string(), + url: hurl!("https://discord.doesnt.exist.com"), // <== Get your discord webhook url + }), + ], + }), + ], + application, }; - // 2. Enhance with extra scores (monitoring, CI/CD, …) - let mut monitoring = MonitoringAlertingStackScore::new(); - monitoring.namespace = Some(lamp_stack.config.namespace.clone()); - - // 3. Run your scores on the desired topology & inventory harmony_cli::run( - Inventory::autoload(), // auto-detect hardware / kube-config - K8sAnywhereTopology::from_env(), // local k3d, CI, staging, prod… - vec![ - Box::new(lamp_stack), - Box::new(monitoring) - ], - None - ).await.unwrap(); + Inventory::autoload(), + K8sAnywhereTopology::from_env(), // <== Deploy to local automatically provisioned local k3d by default or connect to any kubernetes cluster + vec![Box::new(app)], + None, + ) + .await + .unwrap(); } ``` diff --git a/demos/cncf-k8s-quebec-meetup-september-2025/qrcode_gitea_nationtech.png b/demos/cncf-k8s-quebec-meetup-september-2025/qrcode_gitea_nationtech.png new file mode 100644 index 0000000..4ad8de5 Binary files /dev/null and b/demos/cncf-k8s-quebec-meetup-september-2025/qrcode_gitea_nationtech.png differ diff --git a/demos/cncf-k8s-quebec-meetup-september-2025/slides.html b/demos/cncf-k8s-quebec-meetup-september-2025/slides.html index 5932e1d..1636148 100644 --- a/demos/cncf-k8s-quebec-meetup-september-2025/slides.html +++ b/demos/cncf-k8s-quebec-meetup-september-2025/slides.html @@ -1,4 +1,4 @@ -Voici l'histoire de Petit Poisson
+

Voici l'histoire de Petit Poisson

-
+
-
+
-
+

https://tryrust.org

-
+
-
+
-
+
-
+
-
+

Demo time

-
+
-
+
-
+

Ansible❓

-
+
- name: Download wikipedia
   hosts: localhost
@@ -59,40 +59,40 @@
         mode: '0900'
 
-
+
ansible-lint download.yml
 
 Passed: 0 failure(s), 0 warning(s) on 1 files. Last profile that met the validation criteria was 'production'.
 
-
+
git push
 
-
+
-
+
-
+
-
+
-
+
-
+
-
+

Terraform❓❗

-
+
provider "docker" {}
@@ -106,7 +106,7 @@ resource "docker_network" "invalid_network" {
 }
 
-
+
terraform plan
@@ -136,14 +136,14 @@ Terraform will perform the following actions:
 Plan: 1 to add, 0 to change, 0 to destroy.
 
-
+

✅

-
+
terraform apply
 
-
+
Plan: 1 to add, 0 to change, 0 to destroy.
 
 Do you want to perform these actions?
@@ -153,7 +153,7 @@ Do you want to perform these actions?
   Enter a value: yes
 
-
+
docker_network.invalid_network: Creating...
 ╷
 │ Error: Unable to create network: Error response from daemon: invalid network config:
@@ -166,23 +166,30 @@ Do you want to perform these actions?
 ╵
 
-
+
-
+
-
+

Harmony❓❗

-
+

Demo time

-
+
+
+

🎼

+

Harmony : https://git.nationtech.io/nationtech/harmony

+ +

LinkedIn : https://www.linkedin.com/in/jean-gabriel-gill-couture/

+

Courriel : jg@nationtech.io

+
\ No newline at end of file diff --git a/demos/cncf-k8s-quebec-meetup-september-2025/slides.md b/demos/cncf-k8s-quebec-meetup-september-2025/slides.md index 3906755..2060883 100644 --- a/demos/cncf-k8s-quebec-meetup-september-2025/slides.md +++ b/demos/cncf-k8s-quebec-meetup-september-2025/slides.md @@ -225,3 +225,17 @@ Demo time --- + +--- + +# 🎼 + +Harmony : [https://git.nationtech.io/nationtech/harmony](https://git.nationtech.io/nationtech/harmony) + + + + + +LinkedIn : [https://www.linkedin.com/in/jean-gabriel-gill-couture/](https://www.linkedin.com/in/jean-gabriel-gill-couture/) + +Courriel : [jg@nationtech.io](mailto:jg@nationtech.io) diff --git a/examples/try_rust_webapp/src/main.rs b/examples/try_rust_webapp/src/main.rs index 977ca16..56a058d 100644 --- a/examples/try_rust_webapp/src/main.rs +++ b/examples/try_rust_webapp/src/main.rs @@ -16,16 +16,13 @@ use std::{path::PathBuf, sync::Arc}; async fn main() { let application = Arc::new(RustWebapp { name: "harmony-example-tryrust".to_string(), - project_root: PathBuf::from("./tryrust.org"), + project_root: PathBuf::from("./tryrust.org"), // <== Project root, in this case it is a + // submodule framework: Some(RustWebFramework::Leptos), service_port: 8080, }); - let discord_receiver = DiscordWebhook { - name: "test-discord".to_string(), - url: hurl!("https://discord.doesnt.exist.com"), - }; - + // Define your Application deployment and the features you want let app = ApplicationScore { features: vec![ Box::new(PackagingDeployment { @@ -33,7 +30,10 @@ async fn main() { }), Box::new(Monitoring { application: application.clone(), - alert_receiver: vec![Box::new(discord_receiver)], + alert_receiver: vec![Box::new(DiscordWebhook { + name: "test-discord".to_string(), + url: hurl!("https://discord.doesnt.exist.com"), + })], }), ], application, @@ -41,7 +41,7 @@ async fn main() { harmony_cli::run( Inventory::autoload(), - K8sAnywhereTopology::from_env(), + K8sAnywhereTopology::from_env(), // <== Deploy to local automatically provisioned k3d by default or connect to any kubernetes cluster vec![Box::new(app)], None, ) diff --git a/harmony/src/modules/application/features/helm_argocd_score.rs b/harmony/src/modules/application/features/helm_argocd_score.rs index 84aff9a..26767e1 100644 --- a/harmony/src/modules/application/features/helm_argocd_score.rs +++ b/harmony/src/modules/application/features/helm_argocd_score.rs @@ -55,7 +55,8 @@ impl Interpret for ArgoInter topology: &T, ) -> Result { let k8s_client = topology.k8s_client().await?; - let domain = topology.get_domain("argo").await?; + let svc = format!("argo-{}", self.score.namespace.clone()); + let domain = topology.get_domain(&svc).await?; let helm_score = argo_helm_chart_score(&self.score.namespace, self.score.openshift, &domain); @@ -66,14 +67,17 @@ impl Interpret for ArgoInter .await .unwrap(); - Ok(Outcome::success(format!( - "ArgoCD installed with {} {}", - self.argo_apps.len(), - match self.argo_apps.len() { - 1 => "application", - _ => "applications", - } - ))) + Ok(Outcome::success_with_details( + format!( + "ArgoCD {} {}", + self.argo_apps.len(), + match self.argo_apps.len() { + 1 => "application", + _ => "applications", + } + ), + vec![format!("argo application: http://{}", domain)], + )) } fn get_name(&self) -> InterpretName { diff --git a/harmony/src/modules/k8s/ingress.rs b/harmony/src/modules/k8s/ingress.rs index d94a1aa..045f1f8 100644 --- a/harmony/src/modules/k8s/ingress.rs +++ b/harmony/src/modules/k8s/ingress.rs @@ -141,7 +141,10 @@ impl Interpret for K8sIngressInterpret { InterpretStatus::SUCCESS => { let details = match &self.namespace { Some(namespace) => { - vec![format!("{} ({namespace}): {}", self.service, self.host)] + vec![format!( + "{} ({namespace}): http://{}", + self.service, self.host + )] } None => vec![format!("{}: {}", self.service, self.host)], };