Compare commits
	
		
			No commits in common. "c64b59a4ec2ec4df1066dc254e1dc74026a25174" and "024084859e267877929c7d651994dd9cd1b9d25f" have entirely different histories.
		
	
	
		
			c64b59a4ec
			...
			024084859e
		
	
		
							
								
								
									
										2
									
								
								check.sh
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								check.sh
									
									
									
									
									
								
							| @ -1,7 +1,5 @@ | |||||||
| #!/bin/sh | #!/bin/sh | ||||||
| set -e | set -e | ||||||
| 
 |  | ||||||
| cargo check --all-targets --all-features --keep-going | cargo check --all-targets --all-features --keep-going | ||||||
| cargo fmt --check | cargo fmt --check | ||||||
| cargo clippy |  | ||||||
| cargo test | cargo test | ||||||
|  | |||||||
| @ -1,11 +1,17 @@ | |||||||
| use std::{path::PathBuf, str::FromStr, sync::Arc}; | use std::{path::PathBuf, sync::Arc}; | ||||||
| 
 | 
 | ||||||
| use harmony::{ | use harmony::{ | ||||||
|     data::Id, |     data::Id, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|  |     maestro::Maestro, | ||||||
|     modules::{ |     modules::{ | ||||||
|         application::{ApplicationScore, RustWebFramework, RustWebapp, features::Monitoring}, |         application::{ | ||||||
|         monitoring::alert_channel::webhook_receiver::WebhookReceiver, |             ApplicationScore, RustWebFramework, RustWebapp, | ||||||
|  |             features::{ContinuousDelivery, Monitoring}, | ||||||
|  |         }, | ||||||
|  |         monitoring::alert_channel::{ | ||||||
|  |             discord_alert_channel::DiscordWebhook, webhook_receiver::WebhookReceiver, | ||||||
|  |         }, | ||||||
|         tenant::TenantScore, |         tenant::TenantScore, | ||||||
|     }, |     }, | ||||||
|     topology::{K8sAnywhereTopology, Url, tenant::TenantConfig}, |     topology::{K8sAnywhereTopology, Url, tenant::TenantConfig}, | ||||||
| @ -19,7 +25,7 @@ async fn main() { | |||||||
|     //the TenantConfig.name must match
 |     //the TenantConfig.name must match
 | ||||||
|     let tenant = TenantScore { |     let tenant = TenantScore { | ||||||
|         config: TenantConfig { |         config: TenantConfig { | ||||||
|             id: Id::from_str("test-tenant-id").unwrap(), |             id: Id::from_str("test-tenant-id"), | ||||||
|             name: "example-monitoring".to_string(), |             name: "example-monitoring".to_string(), | ||||||
|             ..Default::default() |             ..Default::default() | ||||||
|         }, |         }, | ||||||
|  | |||||||
| @ -125,47 +125,40 @@ spec: | |||||||
|                   name: nginx"#,
 |                   name: nginx"#,
 | ||||||
|     ) |     ) | ||||||
|     .unwrap(); |     .unwrap(); | ||||||
|     deployment |     return deployment; | ||||||
| } | } | ||||||
| fn nginx_deployment_2() -> Deployment { | fn nginx_deployment_2() -> Deployment { | ||||||
|     let pod_template = PodTemplateSpec { |     let mut pod_template = PodTemplateSpec::default(); | ||||||
|         metadata: Some(ObjectMeta { |     pod_template.metadata = Some(ObjectMeta { | ||||||
|             labels: Some(BTreeMap::from([( |         labels: Some(BTreeMap::from([( | ||||||
|                 "app".to_string(), |             "app".to_string(), | ||||||
|                 "nginx-test".to_string(), |             "nginx-test".to_string(), | ||||||
|             )])), |         )])), | ||||||
|  |         ..Default::default() | ||||||
|  |     }); | ||||||
|  |     pod_template.spec = Some(PodSpec { | ||||||
|  |         containers: vec![Container { | ||||||
|  |             name: "nginx".to_string(), | ||||||
|  |             image: Some("nginx".to_string()), | ||||||
|             ..Default::default() |             ..Default::default() | ||||||
|         }), |         }], | ||||||
|         spec: Some(PodSpec { |         ..Default::default() | ||||||
|             containers: vec![Container { |     }); | ||||||
|                 name: "nginx".to_string(), |     let mut spec = DeploymentSpec::default(); | ||||||
|                 image: Some("nginx".to_string()), |     spec.template = pod_template; | ||||||
|                 ..Default::default() |     spec.selector = LabelSelector { | ||||||
|             }], |         match_expressions: None, | ||||||
|             ..Default::default() |         match_labels: Some(BTreeMap::from([( | ||||||
|         }), |             "app".to_string(), | ||||||
|  |             "nginx-test".to_string(), | ||||||
|  |         )])), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     let spec = DeploymentSpec { |     let mut deployment = Deployment::default(); | ||||||
|         template: pod_template, |     deployment.spec = Some(spec); | ||||||
|         selector: LabelSelector { |     deployment.metadata.name = Some("nginx-test".to_string()); | ||||||
|             match_expressions: None, |  | ||||||
|             match_labels: Some(BTreeMap::from([( |  | ||||||
|                 "app".to_string(), |  | ||||||
|                 "nginx-test".to_string(), |  | ||||||
|             )])), |  | ||||||
|         }, |  | ||||||
|         ..Default::default() |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     Deployment { |     deployment | ||||||
|         spec: Some(spec), |  | ||||||
|         metadata: ObjectMeta { |  | ||||||
|             name: Some("nginx-test".to_string()), |  | ||||||
|             ..Default::default() |  | ||||||
|         }, |  | ||||||
|         ..Default::default() |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn nginx_deployment() -> Deployment { | fn nginx_deployment() -> Deployment { | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ async fn main() { | |||||||
|         // This config can be extended as needed for more complicated configurations
 |         // This config can be extended as needed for more complicated configurations
 | ||||||
|         config: LAMPConfig { |         config: LAMPConfig { | ||||||
|             project_root: "./php".into(), |             project_root: "./php".into(), | ||||||
|             database_size: "4Gi".to_string().into(), |             database_size: format!("4Gi").into(), | ||||||
|             ..Default::default() |             ..Default::default() | ||||||
|         }, |         }, | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| use std::{collections::HashMap, str::FromStr}; | use std::collections::HashMap; | ||||||
| 
 | 
 | ||||||
| use harmony::{ | use harmony::{ | ||||||
|     data::Id, |     data::Id, | ||||||
| @ -28,7 +28,7 @@ use harmony::{ | |||||||
| async fn main() { | async fn main() { | ||||||
|     let tenant = TenantScore { |     let tenant = TenantScore { | ||||||
|         config: TenantConfig { |         config: TenantConfig { | ||||||
|             id: Id::from_str("1234").unwrap(), |             id: Id::from_string("1234".to_string()), | ||||||
|             name: "test-tenant".to_string(), |             name: "test-tenant".to_string(), | ||||||
|             resource_limits: ResourceLimits { |             resource_limits: ResourceLimits { | ||||||
|                 cpu_request_cores: 6.0, |                 cpu_request_cores: 6.0, | ||||||
|  | |||||||
| @ -1,5 +1,3 @@ | |||||||
| use std::str::FromStr; |  | ||||||
| 
 |  | ||||||
| use harmony::{ | use harmony::{ | ||||||
|     data::Id, |     data::Id, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
| @ -11,7 +9,7 @@ use harmony::{ | |||||||
| async fn main() { | async fn main() { | ||||||
|     let tenant = TenantScore { |     let tenant = TenantScore { | ||||||
|         config: TenantConfig { |         config: TenantConfig { | ||||||
|             id: Id::from_str("test-tenant-id").unwrap(), |             id: Id::from_str("test-tenant-id"), | ||||||
|             name: "testtenant".to_string(), |             name: "testtenant".to_string(), | ||||||
|             ..Default::default() |             ..Default::default() | ||||||
|         }, |         }, | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| use rand::distr::Alphanumeric; | use rand::distr::Alphanumeric; | ||||||
| use rand::distr::SampleString; | use rand::distr::SampleString; | ||||||
| use std::str::FromStr; |  | ||||||
| use std::time::SystemTime; | use std::time::SystemTime; | ||||||
| use std::time::UNIX_EPOCH; | use std::time::UNIX_EPOCH; | ||||||
| 
 | 
 | ||||||
| @ -24,13 +23,13 @@ pub struct Id { | |||||||
|     value: String, |     value: String, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl FromStr for Id { | impl Id { | ||||||
|     type Err = (); |     pub fn from_string(value: String) -> Self { | ||||||
|  |         Self { value } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { |     pub fn from_str(value: &str) -> Self { | ||||||
|         Ok(Id { |         Self::from_string(value.to_string()) | ||||||
|             value: s.to_string(), |  | ||||||
|         }) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -47,7 +47,7 @@ impl serde::Serialize for Version { | |||||||
| 
 | 
 | ||||||
| impl std::fmt::Display for Version { | impl std::fmt::Display for Version { | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|         self.value.fmt(f) |         return self.value.fmt(f); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -35,9 +35,10 @@ impl PhysicalHost { | |||||||
| 
 | 
 | ||||||
|     pub fn cluster_mac(&self) -> MacAddress { |     pub fn cluster_mac(&self) -> MacAddress { | ||||||
|         self.network |         self.network | ||||||
|             .first() |             .get(0) | ||||||
|             .expect("Cluster physical host should have a network interface") |             .expect("Cluster physical host should have a network interface") | ||||||
|             .mac_address |             .mac_address | ||||||
|  |             .clone() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn cpu(mut self, cpu_count: Option<u64>) -> Self { |     pub fn cpu(mut self, cpu_count: Option<u64>) -> Self { | ||||||
|  | |||||||
| @ -70,7 +70,10 @@ impl<T: Topology> Maestro<T> { | |||||||
|     fn is_topology_initialized(&self) -> bool { |     fn is_topology_initialized(&self) -> bool { | ||||||
|         let result = self.topology_preparation_result.lock().unwrap(); |         let result = self.topology_preparation_result.lock().unwrap(); | ||||||
|         if let Some(outcome) = result.as_ref() { |         if let Some(outcome) = result.as_ref() { | ||||||
|             matches!(outcome.status, InterpretStatus::SUCCESS) |             match outcome.status { | ||||||
|  |                 InterpretStatus::SUCCESS => return true, | ||||||
|  |                 _ => return false, | ||||||
|  |             } | ||||||
|         } else { |         } else { | ||||||
|             false |             false | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ pub trait SerializeScore<T: Topology> { | |||||||
|     fn serialize(&self) -> Value; |     fn serialize(&self) -> Value; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<S, T> SerializeScore<T> for S | impl<'de, S, T> SerializeScore<T> for S | ||||||
| where | where | ||||||
|     T: Topology, |     T: Topology, | ||||||
|     S: Score<T> + Serialize, |     S: Score<T> + Serialize, | ||||||
| @ -24,7 +24,7 @@ where | |||||||
|     fn serialize(&self) -> Value { |     fn serialize(&self) -> Value { | ||||||
|         // TODO not sure if this is the right place to handle the error or it should bubble
 |         // TODO not sure if this is the right place to handle the error or it should bubble
 | ||||||
|         // up?
 |         // up?
 | ||||||
|         serde_value::to_value(self).expect("Score should serialize successfully") |         serde_value::to_value(&self).expect("Score should serialize successfully") | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| use derive_new::new; | use derive_new::new; | ||||||
|  | use futures_util::StreamExt; | ||||||
| use k8s_openapi::{ | use k8s_openapi::{ | ||||||
|     ClusterResourceScope, NamespaceResourceScope, |     ClusterResourceScope, NamespaceResourceScope, | ||||||
|     api::{apps::v1::Deployment, core::v1::Pod}, |     api::{apps::v1::Deployment, core::v1::Pod}, | ||||||
| @ -17,7 +18,7 @@ use kube::{ | |||||||
| }; | }; | ||||||
| use log::{debug, error, trace}; | use log::{debug, error, trace}; | ||||||
| use serde::{Serialize, de::DeserializeOwned}; | use serde::{Serialize, de::DeserializeOwned}; | ||||||
| use similar::TextDiff; | use similar::{DiffableStr, TextDiff}; | ||||||
| 
 | 
 | ||||||
| #[derive(new, Clone)] | #[derive(new, Clone)] | ||||||
| pub struct K8sClient { | pub struct K8sClient { | ||||||
| @ -66,13 +67,13 @@ impl K8sClient { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let establish = await_condition(api, name.as_str(), conditions::is_deployment_completed()); |         let establish = await_condition(api, name.as_str(), conditions::is_deployment_completed()); | ||||||
|         let t = timeout.unwrap_or(300); |         let t = if let Some(t) = timeout { t } else { 300 }; | ||||||
|         let res = tokio::time::timeout(std::time::Duration::from_secs(t), establish).await; |         let res = tokio::time::timeout(std::time::Duration::from_secs(t), establish).await; | ||||||
| 
 | 
 | ||||||
|         if res.is_ok() { |         if let Ok(r) = res { | ||||||
|             Ok(()) |             return Ok(()); | ||||||
|         } else { |         } else { | ||||||
|             Err("timed out while waiting for deployment".to_string()) |             return Err("timed out while waiting for deployment".to_string()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -111,7 +112,7 @@ impl K8sClient { | |||||||
|             .await; |             .await; | ||||||
| 
 | 
 | ||||||
|         match res { |         match res { | ||||||
|             Err(e) => Err(e.to_string()), |             Err(e) => return Err(e.to_string()), | ||||||
|             Ok(mut process) => { |             Ok(mut process) => { | ||||||
|                 let status = process |                 let status = process | ||||||
|                     .take_status() |                     .take_status() | ||||||
| @ -121,9 +122,13 @@ impl K8sClient { | |||||||
| 
 | 
 | ||||||
|                 if let Some(s) = status.status { |                 if let Some(s) = status.status { | ||||||
|                     debug!("Status: {}", s); |                     debug!("Status: {}", s); | ||||||
|                     if s == "Success" { Ok(()) } else { Err(s) } |                     if s == "Success" { | ||||||
|  |                         return Ok(()); | ||||||
|  |                     } else { | ||||||
|  |                         return Err(s); | ||||||
|  |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     Err("Couldn't get inner status of pod exec".to_string()) |                     return Err("Couldn't get inner status of pod exec".to_string()); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -164,9 +169,8 @@ impl K8sClient { | |||||||
|                     trace!("Received current value {current:#?}"); |                     trace!("Received current value {current:#?}"); | ||||||
|                     // The resource exists, so we calculate and display a diff.
 |                     // The resource exists, so we calculate and display a diff.
 | ||||||
|                     println!("\nPerforming dry-run for resource: '{}'", name); |                     println!("\nPerforming dry-run for resource: '{}'", name); | ||||||
|                     let mut current_yaml = serde_yaml::to_value(¤t).unwrap_or_else(|_| { |                     let mut current_yaml = serde_yaml::to_value(¤t) | ||||||
|                         panic!("Could not serialize current value : {current:#?}") |                         .expect(&format!("Could not serialize current value : {current:#?}")); | ||||||
|                     }); |  | ||||||
|                     if current_yaml.is_mapping() && current_yaml.get("status").is_some() { |                     if current_yaml.is_mapping() && current_yaml.get("status").is_some() { | ||||||
|                         let map = current_yaml.as_mapping_mut().unwrap(); |                         let map = current_yaml.as_mapping_mut().unwrap(); | ||||||
|                         let removed = map.remove_entry("status"); |                         let removed = map.remove_entry("status"); | ||||||
| @ -233,7 +237,7 @@ impl K8sClient { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn apply_many<K>(&self, resource: &[K], ns: Option<&str>) -> Result<Vec<K>, Error> |     pub async fn apply_many<K>(&self, resource: &Vec<K>, ns: Option<&str>) -> Result<Vec<K>, Error> | ||||||
|     where |     where | ||||||
|         K: Resource + Clone + std::fmt::Debug + DeserializeOwned + serde::Serialize, |         K: Resource + Clone + std::fmt::Debug + DeserializeOwned + serde::Serialize, | ||||||
|         <K as Resource>::Scope: ApplyStrategy<K>, |         <K as Resource>::Scope: ApplyStrategy<K>, | ||||||
| @ -249,7 +253,7 @@ impl K8sClient { | |||||||
| 
 | 
 | ||||||
|     pub async fn apply_yaml_many( |     pub async fn apply_yaml_many( | ||||||
|         &self, |         &self, | ||||||
|         #[allow(clippy::ptr_arg)] yaml: &Vec<serde_yaml::Value>, |         yaml: &Vec<serde_yaml::Value>, | ||||||
|         ns: Option<&str>, |         ns: Option<&str>, | ||||||
|     ) -> Result<(), Error> { |     ) -> Result<(), Error> { | ||||||
|         for y in yaml.iter() { |         for y in yaml.iter() { | ||||||
|  | |||||||
| @ -87,9 +87,7 @@ impl PrometheusApplicationMonitoring<CRDPrometheus> for K8sAnywhereTopology { | |||||||
|             .execute(inventory, self) |             .execute(inventory, self) | ||||||
|             .await?; |             .await?; | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success( |         Ok(Outcome::success(format!("No action, working on cluster  "))) | ||||||
|             "No action, working on cluster  ".to_string(), |  | ||||||
|         )) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -126,7 +124,7 @@ impl K8sAnywhereTopology { | |||||||
|     ) -> K8sPrometheusCRDAlertingScore { |     ) -> K8sPrometheusCRDAlertingScore { | ||||||
|         K8sPrometheusCRDAlertingScore { |         K8sPrometheusCRDAlertingScore { | ||||||
|             sender, |             sender, | ||||||
|             receivers: receivers.unwrap_or_default(), |             receivers: receivers.unwrap_or_else(Vec::new), | ||||||
|             service_monitors: vec![], |             service_monitors: vec![], | ||||||
|             prometheus_rules: vec![], |             prometheus_rules: vec![], | ||||||
|         } |         } | ||||||
| @ -178,7 +176,7 @@ impl K8sAnywhereTopology { | |||||||
|         } else { |         } else { | ||||||
|             if let Some(kubeconfig) = &k8s_anywhere_config.kubeconfig { |             if let Some(kubeconfig) = &k8s_anywhere_config.kubeconfig { | ||||||
|                 debug!("Loading kubeconfig {kubeconfig}"); |                 debug!("Loading kubeconfig {kubeconfig}"); | ||||||
|                 match self.try_load_kubeconfig(kubeconfig).await { |                 match self.try_load_kubeconfig(&kubeconfig).await { | ||||||
|                     Some(client) => { |                     Some(client) => { | ||||||
|                         return Ok(Some(K8sState { |                         return Ok(Some(K8sState { | ||||||
|                             client: Arc::new(client), |                             client: Arc::new(client), | ||||||
| @ -234,7 +232,7 @@ impl K8sAnywhereTopology { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn ensure_k8s_tenant_manager(&self) -> Result<(), String> { |     async fn ensure_k8s_tenant_manager(&self) -> Result<(), String> { | ||||||
|         if self.tenant_manager.get().is_some() { |         if let Some(_) = self.tenant_manager.get() { | ||||||
|             return Ok(()); |             return Ok(()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -368,7 +366,7 @@ impl Topology for K8sAnywhereTopology { | |||||||
| 
 | 
 | ||||||
|         self.ensure_k8s_tenant_manager() |         self.ensure_k8s_tenant_manager() | ||||||
|             .await |             .await | ||||||
|             .map_err(InterpretError::new)?; |             .map_err(|e| InterpretError::new(e))?; | ||||||
| 
 | 
 | ||||||
|         match self.is_helm_available() { |         match self.is_helm_available() { | ||||||
|             Ok(()) => Ok(Outcome::success(format!( |             Ok(()) => Ok(Outcome::success(format!( | ||||||
|  | |||||||
| @ -88,7 +88,7 @@ impl Serialize for Url { | |||||||
|     { |     { | ||||||
|         match self { |         match self { | ||||||
|             Url::LocalFolder(path) => serializer.serialize_str(path), |             Url::LocalFolder(path) => serializer.serialize_str(path), | ||||||
|             Url::Url(url) => serializer.serialize_str(url.as_str()), |             Url::Url(url) => serializer.serialize_str(&url.as_str()), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -27,11 +27,11 @@ pub struct UnmanagedRouter { | |||||||
| 
 | 
 | ||||||
| impl Router for UnmanagedRouter { | impl Router for UnmanagedRouter { | ||||||
|     fn get_gateway(&self) -> IpAddress { |     fn get_gateway(&self) -> IpAddress { | ||||||
|         self.gateway |         self.gateway.clone() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn get_cidr(&self) -> Ipv4Cidr { |     fn get_cidr(&self) -> Ipv4Cidr { | ||||||
|         self.cidr |         self.cidr.clone() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn get_host(&self) -> LogicalHost { |     fn get_host(&self) -> LogicalHost { | ||||||
|  | |||||||
| @ -309,19 +309,19 @@ impl K8sTenantManager { | |||||||
|                 let ports: Option<Vec<NetworkPolicyPort>> = |                 let ports: Option<Vec<NetworkPolicyPort>> = | ||||||
|                     c.1.as_ref().map(|spec| match &spec.data { |                     c.1.as_ref().map(|spec| match &spec.data { | ||||||
|                         super::PortSpecData::SinglePort(port) => vec![NetworkPolicyPort { |                         super::PortSpecData::SinglePort(port) => vec![NetworkPolicyPort { | ||||||
|                             port: Some(IntOrString::Int((*port).into())), |                             port: Some(IntOrString::Int(port.clone().into())), | ||||||
|                             ..Default::default() |                             ..Default::default() | ||||||
|                         }], |                         }], | ||||||
|                         super::PortSpecData::PortRange(start, end) => vec![NetworkPolicyPort { |                         super::PortSpecData::PortRange(start, end) => vec![NetworkPolicyPort { | ||||||
|                             port: Some(IntOrString::Int((*start).into())), |                             port: Some(IntOrString::Int(start.clone().into())), | ||||||
|                             end_port: Some((*end).into()), |                             end_port: Some(end.clone().into()), | ||||||
|                             protocol: None, // Not currently supported by Harmony
 |                             protocol: None, // Not currently supported by Harmony
 | ||||||
|                         }], |                         }], | ||||||
| 
 | 
 | ||||||
|                         super::PortSpecData::ListOfPorts(items) => items |                         super::PortSpecData::ListOfPorts(items) => items | ||||||
|                             .iter() |                             .iter() | ||||||
|                             .map(|i| NetworkPolicyPort { |                             .map(|i| NetworkPolicyPort { | ||||||
|                                 port: Some(IntOrString::Int((*i).into())), |                                 port: Some(IntOrString::Int(i.clone().into())), | ||||||
|                                 ..Default::default() |                                 ..Default::default() | ||||||
|                             }) |                             }) | ||||||
|                             .collect(), |                             .collect(), | ||||||
| @ -366,19 +366,19 @@ impl K8sTenantManager { | |||||||
|                 let ports: Option<Vec<NetworkPolicyPort>> = |                 let ports: Option<Vec<NetworkPolicyPort>> = | ||||||
|                     c.1.as_ref().map(|spec| match &spec.data { |                     c.1.as_ref().map(|spec| match &spec.data { | ||||||
|                         super::PortSpecData::SinglePort(port) => vec![NetworkPolicyPort { |                         super::PortSpecData::SinglePort(port) => vec![NetworkPolicyPort { | ||||||
|                             port: Some(IntOrString::Int((*port).into())), |                             port: Some(IntOrString::Int(port.clone().into())), | ||||||
|                             ..Default::default() |                             ..Default::default() | ||||||
|                         }], |                         }], | ||||||
|                         super::PortSpecData::PortRange(start, end) => vec![NetworkPolicyPort { |                         super::PortSpecData::PortRange(start, end) => vec![NetworkPolicyPort { | ||||||
|                             port: Some(IntOrString::Int((*start).into())), |                             port: Some(IntOrString::Int(start.clone().into())), | ||||||
|                             end_port: Some((*end).into()), |                             end_port: Some(end.clone().into()), | ||||||
|                             protocol: None, // Not currently supported by Harmony
 |                             protocol: None, // Not currently supported by Harmony
 | ||||||
|                         }], |                         }], | ||||||
| 
 | 
 | ||||||
|                         super::PortSpecData::ListOfPorts(items) => items |                         super::PortSpecData::ListOfPorts(items) => items | ||||||
|                             .iter() |                             .iter() | ||||||
|                             .map(|i| NetworkPolicyPort { |                             .map(|i| NetworkPolicyPort { | ||||||
|                                 port: Some(IntOrString::Int((*i).into())), |                                 port: Some(IntOrString::Int(i.clone().into())), | ||||||
|                                 ..Default::default() |                                 ..Default::default() | ||||||
|                             }) |                             }) | ||||||
|                             .collect(), |                             .collect(), | ||||||
|  | |||||||
| @ -60,7 +60,7 @@ impl DnsServer for OPNSenseFirewall { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn get_ip(&self) -> IpAddress { |     fn get_ip(&self) -> IpAddress { | ||||||
|         OPNSenseFirewall::get_ip(self) |         OPNSenseFirewall::get_ip(&self) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn get_host(&self) -> LogicalHost { |     fn get_host(&self) -> LogicalHost { | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ impl HttpServer for OPNSenseFirewall { | |||||||
|     async fn ensure_initialized(&self) -> Result<(), ExecutorError> { |     async fn ensure_initialized(&self) -> Result<(), ExecutorError> { | ||||||
|         let mut config = self.opnsense_config.write().await; |         let mut config = self.opnsense_config.write().await; | ||||||
|         let caddy = config.caddy(); |         let caddy = config.caddy(); | ||||||
|         if caddy.get_full_config().is_none() { |         if let None = caddy.get_full_config() { | ||||||
|             info!("Http config not available in opnsense config, installing package"); |             info!("Http config not available in opnsense config, installing package"); | ||||||
|             config.install_package("os-caddy").await.map_err(|e| { |             config.install_package("os-caddy").await.map_err(|e| { | ||||||
|                 ExecutorError::UnexpectedError(format!( |                 ExecutorError::UnexpectedError(format!( | ||||||
|  | |||||||
| @ -121,12 +121,10 @@ pub(crate) fn haproxy_xml_config_to_harmony_loadbalancer( | |||||||
| 
 | 
 | ||||||
|             LoadBalancerService { |             LoadBalancerService { | ||||||
|                 backend_servers, |                 backend_servers, | ||||||
|                 listening_port: frontend.bind.parse().unwrap_or_else(|_| { |                 listening_port: frontend.bind.parse().expect(&format!( | ||||||
|                     panic!( |                     "HAProxy frontend address should be a valid SocketAddr, got {}", | ||||||
|                         "HAProxy frontend address should be a valid SocketAddr, got {}", |                     frontend.bind | ||||||
|                         frontend.bind |                 )), | ||||||
|                     ) |  | ||||||
|                 }), |  | ||||||
|                 health_check, |                 health_check, | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
| @ -169,28 +167,28 @@ pub(crate) fn get_health_check_for_backend( | |||||||
|         None => return None, |         None => return None, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     let haproxy_health_check = haproxy |     let haproxy_health_check = match haproxy | ||||||
|         .healthchecks |         .healthchecks | ||||||
|         .healthchecks |         .healthchecks | ||||||
|         .iter() |         .iter() | ||||||
|         .find(|h| &h.uuid == health_check_uuid)?; |         .find(|h| &h.uuid == health_check_uuid) | ||||||
|  |     { | ||||||
|  |         Some(health_check) => health_check, | ||||||
|  |         None => return None, | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     let binding = haproxy_health_check.health_check_type.to_uppercase(); |     let binding = haproxy_health_check.health_check_type.to_uppercase(); | ||||||
|     let uppercase = binding.as_str(); |     let uppercase = binding.as_str(); | ||||||
|     match uppercase { |     match uppercase { | ||||||
|         "TCP" => { |         "TCP" => { | ||||||
|             if let Some(checkport) = haproxy_health_check.checkport.content.as_ref() { |             if let Some(checkport) = haproxy_health_check.checkport.content.as_ref() { | ||||||
|                 if !checkport.is_empty() { |                 if checkport.len() > 0 { | ||||||
|                     return Some(HealthCheck::TCP(Some(checkport.parse().unwrap_or_else( |                     return Some(HealthCheck::TCP(Some(checkport.parse().expect(&format!( | ||||||
|                         |_| { |                         "HAProxy check port should be a valid port number, got {checkport}" | ||||||
|                             panic!( |                     ))))); | ||||||
|                                 "HAProxy check port should be a valid port number, got {checkport}" |  | ||||||
|                             ) |  | ||||||
|                         }, |  | ||||||
|                     )))); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             Some(HealthCheck::TCP(None)) |             return Some(HealthCheck::TCP(None)); | ||||||
|         } |         } | ||||||
|         "HTTP" => { |         "HTTP" => { | ||||||
|             let path: String = haproxy_health_check |             let path: String = haproxy_health_check | ||||||
| @ -357,13 +355,16 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|         // Create an HAProxy instance with servers
 |         // Create an HAProxy instance with servers
 | ||||||
|         let mut haproxy = HAProxy::default(); |         let mut haproxy = HAProxy::default(); | ||||||
|         let server = HAProxyServer { |         let mut server = HAProxyServer::default(); | ||||||
|             uuid: "server1".to_string(), |         server.uuid = "server1".to_string(); | ||||||
|             address: "192.168.1.1".to_string(), |         server.address = "192.168.1.1".to_string(); | ||||||
|             port: 80, |         server.port = 80; | ||||||
|             ..Default::default() | 
 | ||||||
|         }; |  | ||||||
|         haproxy.servers.servers.push(server); |         haproxy.servers.servers.push(server); | ||||||
|  |         let mut server = HAProxyServer::default(); | ||||||
|  |         server.uuid = "server3".to_string(); | ||||||
|  |         server.address = "192.168.1.3".to_string(); | ||||||
|  |         server.port = 8080; | ||||||
| 
 | 
 | ||||||
|         // Call the function
 |         // Call the function
 | ||||||
|         let result = get_servers_for_backend(&backend, &haproxy); |         let result = get_servers_for_backend(&backend, &haproxy); | ||||||
| @ -383,12 +384,10 @@ mod tests { | |||||||
|         let backend = HAProxyBackend::default(); |         let backend = HAProxyBackend::default(); | ||||||
|         // Create an HAProxy instance with servers
 |         // Create an HAProxy instance with servers
 | ||||||
|         let mut haproxy = HAProxy::default(); |         let mut haproxy = HAProxy::default(); | ||||||
|         let server = HAProxyServer { |         let mut server = HAProxyServer::default(); | ||||||
|             uuid: "server1".to_string(), |         server.uuid = "server1".to_string(); | ||||||
|             address: "192.168.1.1".to_string(), |         server.address = "192.168.1.1".to_string(); | ||||||
|             port: 80, |         server.port = 80; | ||||||
|             ..Default::default() |  | ||||||
|         }; |  | ||||||
|         haproxy.servers.servers.push(server); |         haproxy.servers.servers.push(server); | ||||||
|         // Call the function
 |         // Call the function
 | ||||||
|         let result = get_servers_for_backend(&backend, &haproxy); |         let result = get_servers_for_backend(&backend, &haproxy); | ||||||
| @ -403,12 +402,10 @@ mod tests { | |||||||
|         backend.linked_servers.content = Some("server4,server5".to_string()); |         backend.linked_servers.content = Some("server4,server5".to_string()); | ||||||
|         // Create an HAProxy instance with servers
 |         // Create an HAProxy instance with servers
 | ||||||
|         let mut haproxy = HAProxy::default(); |         let mut haproxy = HAProxy::default(); | ||||||
|         let server = HAProxyServer { |         let mut server = HAProxyServer::default(); | ||||||
|             uuid: "server1".to_string(), |         server.uuid = "server1".to_string(); | ||||||
|             address: "192.168.1.1".to_string(), |         server.address = "192.168.1.1".to_string(); | ||||||
|             port: 80, |         server.port = 80; | ||||||
|             ..Default::default() |  | ||||||
|         }; |  | ||||||
|         haproxy.servers.servers.push(server); |         haproxy.servers.servers.push(server); | ||||||
|         // Call the function
 |         // Call the function
 | ||||||
|         let result = get_servers_for_backend(&backend, &haproxy); |         let result = get_servers_for_backend(&backend, &haproxy); | ||||||
| @ -419,28 +416,20 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn test_get_servers_for_backend_multiple_linked_servers() { |     fn test_get_servers_for_backend_multiple_linked_servers() { | ||||||
|         // Create a backend with multiple linked servers
 |         // Create a backend with multiple linked servers
 | ||||||
|         #[allow(clippy::field_reassign_with_default)] |  | ||||||
|         let mut backend = HAProxyBackend::default(); |         let mut backend = HAProxyBackend::default(); | ||||||
|         backend.linked_servers.content = Some("server1,server2".to_string()); |         backend.linked_servers.content = Some("server1,server2".to_string()); | ||||||
|         //
 |  | ||||||
|         // Create an HAProxy instance with matching servers
 |         // Create an HAProxy instance with matching servers
 | ||||||
|         let mut haproxy = HAProxy::default(); |         let mut haproxy = HAProxy::default(); | ||||||
|         let server = HAProxyServer { |         let mut server = HAProxyServer::default(); | ||||||
|             uuid: "server1".to_string(), |         server.uuid = "server1".to_string(); | ||||||
|             address: "some-hostname.test.mcd".to_string(), |         server.address = "some-hostname.test.mcd".to_string(); | ||||||
|             port: 80, |         server.port = 80; | ||||||
|             ..Default::default() |  | ||||||
|         }; |  | ||||||
|         haproxy.servers.servers.push(server); |         haproxy.servers.servers.push(server); | ||||||
| 
 |         let mut server = HAProxyServer::default(); | ||||||
|         let server = HAProxyServer { |         server.uuid = "server2".to_string(); | ||||||
|             uuid: "server2".to_string(), |         server.address = "192.168.1.2".to_string(); | ||||||
|             address: "192.168.1.2".to_string(), |         server.port = 8080; | ||||||
|             port: 8080, |  | ||||||
|             ..Default::default() |  | ||||||
|         }; |  | ||||||
|         haproxy.servers.servers.push(server); |         haproxy.servers.servers.push(server); | ||||||
| 
 |  | ||||||
|         // Call the function
 |         // Call the function
 | ||||||
|         let result = get_servers_for_backend(&backend, &haproxy); |         let result = get_servers_for_backend(&backend, &haproxy); | ||||||
|         // Check the result
 |         // Check the result
 | ||||||
|  | |||||||
| @ -58,7 +58,7 @@ impl TftpServer for OPNSenseFirewall { | |||||||
|     async fn ensure_initialized(&self) -> Result<(), ExecutorError> { |     async fn ensure_initialized(&self) -> Result<(), ExecutorError> { | ||||||
|         let mut config = self.opnsense_config.write().await; |         let mut config = self.opnsense_config.write().await; | ||||||
|         let tftp = config.tftp(); |         let tftp = config.tftp(); | ||||||
|         if tftp.get_full_config().is_none() { |         if let None = tftp.get_full_config() { | ||||||
|             info!("Tftp config not available in opnsense config, installing package"); |             info!("Tftp config not available in opnsense config, installing package"); | ||||||
|             config.install_package("os-tftp").await.map_err(|e| { |             config.install_package("os-tftp").await.map_err(|e| { | ||||||
|                 ExecutorError::UnexpectedError(format!( |                 ExecutorError::UnexpectedError(format!( | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ pub trait ApplicationFeature<T: Topology>: | |||||||
|     fn name(&self) -> String; |     fn name(&self) -> String; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait ApplicationFeatureClone<T: Topology> { | trait ApplicationFeatureClone<T: Topology> { | ||||||
|     fn clone_box(&self) -> Box<dyn ApplicationFeature<T>>; |     fn clone_box(&self) -> Box<dyn ApplicationFeature<T>>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -27,7 +27,7 @@ where | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Topology> Serialize for Box<dyn ApplicationFeature<T>> { | impl<T: Topology> Serialize for Box<dyn ApplicationFeature<T>> { | ||||||
|     fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error> |     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||||
|     where |     where | ||||||
|         S: serde::Serializer, |         S: serde::Serializer, | ||||||
|     { |     { | ||||||
|  | |||||||
| @ -184,11 +184,12 @@ impl ArgoApplication { | |||||||
|     pub fn to_yaml(&self) -> serde_yaml::Value { |     pub fn to_yaml(&self) -> serde_yaml::Value { | ||||||
|         let name = &self.name; |         let name = &self.name; | ||||||
|         let namespace = if let Some(ns) = self.namespace.as_ref() { |         let namespace = if let Some(ns) = self.namespace.as_ref() { | ||||||
|             ns |             &ns | ||||||
|         } else { |         } else { | ||||||
|             "argocd" |             "argocd" | ||||||
|         }; |         }; | ||||||
|         let project = &self.project; |         let project = &self.project; | ||||||
|  |         let source = &self.source; | ||||||
| 
 | 
 | ||||||
|         let yaml_str = format!( |         let yaml_str = format!( | ||||||
|             r#" |             r#" | ||||||
| @ -227,7 +228,7 @@ spec: | |||||||
|             serde_yaml::to_value(&self.source).expect("couldn't serialize source to value"); |             serde_yaml::to_value(&self.source).expect("couldn't serialize source to value"); | ||||||
|         let sync_policy = serde_yaml::to_value(&self.sync_policy) |         let sync_policy = serde_yaml::to_value(&self.sync_policy) | ||||||
|             .expect("couldn't serialize sync_policy to value"); |             .expect("couldn't serialize sync_policy to value"); | ||||||
|         let revision_history_limit = serde_yaml::to_value(self.revision_history_limit) |         let revision_history_limit = serde_yaml::to_value(&self.revision_history_limit) | ||||||
|             .expect("couldn't serialize revision_history_limit to value"); |             .expect("couldn't serialize revision_history_limit to value"); | ||||||
| 
 | 
 | ||||||
|         spec.insert( |         spec.insert( | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ use crate::{ | |||||||
|     data::Version, |     data::Version, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     modules::application::{ |     modules::application::{ | ||||||
|         ApplicationFeature, HelmPackage, OCICompliant, |         Application, ApplicationFeature, HelmPackage, OCICompliant, | ||||||
|         features::{ArgoApplication, ArgoHelmScore}, |         features::{ArgoApplication, ArgoHelmScore}, | ||||||
|     }, |     }, | ||||||
|     score::Score, |     score::Score, | ||||||
|  | |||||||
| @ -986,7 +986,7 @@ commitServer: | |||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     HelmChartScore { |     HelmChartScore { | ||||||
|         namespace: Some(NonBlankString::from_str(namespace).unwrap()), |         namespace: Some(NonBlankString::from_str(&namespace).unwrap()), | ||||||
|         release_name: NonBlankString::from_str("argo-cd").unwrap(), |         release_name: NonBlankString::from_str("argo-cd").unwrap(), | ||||||
|         chart_name: NonBlankString::from_str("argo/argo-cd").unwrap(), |         chart_name: NonBlankString::from_str("argo/argo-cd").unwrap(), | ||||||
|         chart_version: Some(NonBlankString::from_str("8.1.2").unwrap()), |         chart_version: Some(NonBlankString::from_str("8.1.2").unwrap()), | ||||||
|  | |||||||
| @ -81,7 +81,7 @@ impl<A: Application, T: Topology + std::fmt::Debug> Interpret<T> for Application | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Serialize for dyn Application { | impl Serialize for dyn Application { | ||||||
|     fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error> |     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||||
|     where |     where | ||||||
|         S: serde::Serializer, |         S: serde::Serializer, | ||||||
|     { |     { | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| use std::fs; | use std::fs; | ||||||
| use std::path::{Path, PathBuf}; | use std::path::PathBuf; | ||||||
| use std::process; | use std::process; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| @ -174,7 +174,7 @@ impl RustWebapp { | |||||||
|             .platform("linux/x86_64"); |             .platform("linux/x86_64"); | ||||||
| 
 | 
 | ||||||
|         let mut temp_tar_builder = tar::Builder::new(Vec::new()); |         let mut temp_tar_builder = tar::Builder::new(Vec::new()); | ||||||
|         temp_tar_builder |         let _ = temp_tar_builder | ||||||
|             .append_dir_all("", self.project_root.clone()) |             .append_dir_all("", self.project_root.clone()) | ||||||
|             .unwrap(); |             .unwrap(); | ||||||
|         let archive = temp_tar_builder |         let archive = temp_tar_builder | ||||||
| @ -530,7 +530,10 @@ spec: | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Packages a Helm chart directory into a .tgz file.
 |     /// Packages a Helm chart directory into a .tgz file.
 | ||||||
|     fn package_helm_chart(&self, chart_dir: &Path) -> Result<PathBuf, Box<dyn std::error::Error>> { |     fn package_helm_chart( | ||||||
|  |         &self, | ||||||
|  |         chart_dir: &PathBuf, | ||||||
|  |     ) -> Result<PathBuf, Box<dyn std::error::Error>> { | ||||||
|         let chart_dirname = chart_dir.file_name().expect("Should find a chart dirname"); |         let chart_dirname = chart_dir.file_name().expect("Should find a chart dirname"); | ||||||
|         debug!( |         debug!( | ||||||
|             "Launching `helm package {}` cli with CWD {}", |             "Launching `helm package {}` cli with CWD {}", | ||||||
| @ -543,13 +546,14 @@ spec: | |||||||
|         ); |         ); | ||||||
|         let output = process::Command::new("helm") |         let output = process::Command::new("helm") | ||||||
|             .args(["package", chart_dirname.to_str().unwrap()]) |             .args(["package", chart_dirname.to_str().unwrap()]) | ||||||
|             .current_dir(self.project_root.join(".harmony_generated").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()?; |             .output()?; | ||||||
| 
 | 
 | ||||||
|         self.check_output(&output, "Failed to package Helm chart")?; |         self.check_output(&output, "Failed to package Helm chart")?; | ||||||
| 
 | 
 | ||||||
|         // Helm prints the path of the created chart to stdout.
 |         // Helm prints the path of the created chart to stdout.
 | ||||||
|         let tgz_name = String::from_utf8(output.stdout)? |         let tgz_name = String::from_utf8(output.stdout)? | ||||||
|  |             .trim() | ||||||
|             .split_whitespace() |             .split_whitespace() | ||||||
|             .last() |             .last() | ||||||
|             .unwrap_or_default() |             .unwrap_or_default() | ||||||
| @ -569,7 +573,7 @@ spec: | |||||||
|     /// Pushes a packaged Helm chart to an OCI registry.
 |     /// Pushes a packaged Helm chart to an OCI registry.
 | ||||||
|     fn push_helm_chart( |     fn push_helm_chart( | ||||||
|         &self, |         &self, | ||||||
|         packaged_chart_path: &Path, |         packaged_chart_path: &PathBuf, | ||||||
|     ) -> Result<String, Box<dyn std::error::Error>> { |     ) -> Result<String, Box<dyn std::error::Error>> { | ||||||
|         // The chart name is the file stem of the .tgz file
 |         // The chart name is the file stem of the .tgz file
 | ||||||
|         let chart_file_name = packaged_chart_path.file_stem().unwrap().to_str().unwrap(); |         let chart_file_name = packaged_chart_path.file_stem().unwrap().to_str().unwrap(); | ||||||
|  | |||||||
| @ -41,6 +41,6 @@ impl<T: Topology + HelmCommand> Score<T> for CertManagerHelmScore { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn name(&self) -> String { |     fn name(&self) -> String { | ||||||
|         "CertManagerHelmScore".to_string() |         format!("CertManagerHelmScore") | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -111,7 +111,7 @@ impl DhcpInterpret { | |||||||
| 
 | 
 | ||||||
|         let boot_filename_outcome = match &self.score.boot_filename { |         let boot_filename_outcome = match &self.score.boot_filename { | ||||||
|             Some(boot_filename) => { |             Some(boot_filename) => { | ||||||
|                 dhcp_server.set_boot_filename(boot_filename).await?; |                 dhcp_server.set_boot_filename(&boot_filename).await?; | ||||||
|                 Outcome::new( |                 Outcome::new( | ||||||
|                     InterpretStatus::SUCCESS, |                     InterpretStatus::SUCCESS, | ||||||
|                     format!("Dhcp Interpret Set boot filename to {boot_filename}"), |                     format!("Dhcp Interpret Set boot filename to {boot_filename}"), | ||||||
| @ -122,7 +122,7 @@ impl DhcpInterpret { | |||||||
| 
 | 
 | ||||||
|         let filename_outcome = match &self.score.filename { |         let filename_outcome = match &self.score.filename { | ||||||
|             Some(filename) => { |             Some(filename) => { | ||||||
|                 dhcp_server.set_filename(filename).await?; |                 dhcp_server.set_filename(&filename).await?; | ||||||
|                 Outcome::new( |                 Outcome::new( | ||||||
|                     InterpretStatus::SUCCESS, |                     InterpretStatus::SUCCESS, | ||||||
|                     format!("Dhcp Interpret Set filename to {filename}"), |                     format!("Dhcp Interpret Set filename to {filename}"), | ||||||
| @ -133,7 +133,7 @@ impl DhcpInterpret { | |||||||
| 
 | 
 | ||||||
|         let filename64_outcome = match &self.score.filename64 { |         let filename64_outcome = match &self.score.filename64 { | ||||||
|             Some(filename64) => { |             Some(filename64) => { | ||||||
|                 dhcp_server.set_filename64(filename64).await?; |                 dhcp_server.set_filename64(&filename64).await?; | ||||||
|                 Outcome::new( |                 Outcome::new( | ||||||
|                     InterpretStatus::SUCCESS, |                     InterpretStatus::SUCCESS, | ||||||
|                     format!("Dhcp Interpret Set filename64 to {filename64}"), |                     format!("Dhcp Interpret Set filename64 to {filename64}"), | ||||||
| @ -144,7 +144,7 @@ impl DhcpInterpret { | |||||||
| 
 | 
 | ||||||
|         let filenameipxe_outcome = match &self.score.filenameipxe { |         let filenameipxe_outcome = match &self.score.filenameipxe { | ||||||
|             Some(filenameipxe) => { |             Some(filenameipxe) => { | ||||||
|                 dhcp_server.set_filenameipxe(filenameipxe).await?; |                 dhcp_server.set_filenameipxe(&filenameipxe).await?; | ||||||
|                 Outcome::new( |                 Outcome::new( | ||||||
|                     InterpretStatus::SUCCESS, |                     InterpretStatus::SUCCESS, | ||||||
|                     format!("Dhcp Interpret Set filenameipxe to {filenameipxe}"), |                     format!("Dhcp Interpret Set filenameipxe to {filenameipxe}"), | ||||||
| @ -209,7 +209,7 @@ impl<T: DhcpServer> Interpret<T> for DhcpInterpret { | |||||||
| 
 | 
 | ||||||
|         Ok(Outcome::new( |         Ok(Outcome::new( | ||||||
|             InterpretStatus::SUCCESS, |             InterpretStatus::SUCCESS, | ||||||
|             "Dhcp Interpret execution successful".to_string(), |             format!("Dhcp Interpret execution successful"), | ||||||
|         )) |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -112,7 +112,7 @@ impl<T: Topology + DnsServer> Interpret<T> for DnsInterpret { | |||||||
| 
 | 
 | ||||||
|         Ok(Outcome::new( |         Ok(Outcome::new( | ||||||
|             InterpretStatus::SUCCESS, |             InterpretStatus::SUCCESS, | ||||||
|             "Dns Interpret execution successful".to_string(), |             format!("Dns Interpret execution successful"), | ||||||
|         )) |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -90,10 +90,14 @@ impl HelmChartInterpret { | |||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         match add_output.status.success() { |         match add_output.status.success() { | ||||||
|             true => Ok(()), |             true => { | ||||||
|             false => Err(InterpretError::new(format!( |                 return Ok(()); | ||||||
|                 "Failed to add helm repository!\n{full_output}" |             } | ||||||
|             ))), |             false => { | ||||||
|  |                 return Err(InterpretError::new(format!( | ||||||
|  |                     "Failed to add helm repository!\n{full_output}" | ||||||
|  |                 ))); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -208,7 +212,7 @@ impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let res = helm_executor.install_or_upgrade( |         let res = helm_executor.install_or_upgrade( | ||||||
|             ns, |             &ns, | ||||||
|             &self.score.release_name, |             &self.score.release_name, | ||||||
|             &self.score.chart_name, |             &self.score.chart_name, | ||||||
|             self.score.chart_version.as_ref(), |             self.score.chart_version.as_ref(), | ||||||
|  | |||||||
| @ -77,11 +77,14 @@ impl HelmCommandExecutor { | |||||||
|             )?; |             )?; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let out = self.clone().run_command( |         let out = match self.clone().run_command( | ||||||
|             self.chart |             self.chart | ||||||
|                 .clone() |                 .clone() | ||||||
|                 .helm_args(self.globals.chart_home.clone().unwrap()), |                 .helm_args(self.globals.chart_home.clone().unwrap()), | ||||||
|         )?; |         ) { | ||||||
|  |             Ok(out) => out, | ||||||
|  |             Err(e) => return Err(e), | ||||||
|  |         }; | ||||||
| 
 | 
 | ||||||
|         // TODO: don't use unwrap here
 |         // TODO: don't use unwrap here
 | ||||||
|         let s = String::from_utf8(out.stdout).unwrap(); |         let s = String::from_utf8(out.stdout).unwrap(); | ||||||
| @ -95,11 +98,14 @@ impl HelmCommandExecutor { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn version(self) -> Result<String, std::io::Error> { |     pub fn version(self) -> Result<String, std::io::Error> { | ||||||
|         let out = self.run_command(vec![ |         let out = match self.run_command(vec![ | ||||||
|             "version".to_string(), |             "version".to_string(), | ||||||
|             "-c".to_string(), |             "-c".to_string(), | ||||||
|             "--short".to_string(), |             "--short".to_string(), | ||||||
|         ])?; |         ]) { | ||||||
|  |             Ok(out) => out, | ||||||
|  |             Err(e) => return Err(e), | ||||||
|  |         }; | ||||||
| 
 | 
 | ||||||
|         // TODO: don't use unwrap
 |         // TODO: don't use unwrap
 | ||||||
|         Ok(String::from_utf8(out.stdout).unwrap()) |         Ok(String::from_utf8(out.stdout).unwrap()) | ||||||
| @ -123,11 +129,15 @@ impl HelmCommandExecutor { | |||||||
|             None => PathBuf::from(TempDir::new()?.path()), |             None => PathBuf::from(TempDir::new()?.path()), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         if let Some(yaml_str) = self.chart.values_inline { |         match self.chart.values_inline { | ||||||
|             let tf: TempFile = temp_file::with_contents(yaml_str.as_bytes()); |             Some(yaml_str) => { | ||||||
|             self.chart |                 let tf: TempFile; | ||||||
|                 .additional_values_files |                 tf = temp_file::with_contents(yaml_str.as_bytes()); | ||||||
|                 .push(PathBuf::from(tf.path())); |                 self.chart | ||||||
|  |                     .additional_values_files | ||||||
|  |                     .push(PathBuf::from(tf.path())); | ||||||
|  |             } | ||||||
|  |             None => (), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         self.env.insert( |         self.env.insert( | ||||||
| @ -170,9 +180,9 @@ impl HelmChart { | |||||||
|         match self.repo { |         match self.repo { | ||||||
|             Some(r) => { |             Some(r) => { | ||||||
|                 if r.starts_with("oci://") { |                 if r.starts_with("oci://") { | ||||||
|                     args.push( |                     args.push(String::from( | ||||||
|                         r.trim_end_matches("/").to_string() + "/" + self.name.clone().as_str(), |                         r.trim_end_matches("/").to_string() + "/" + self.name.clone().as_str(), | ||||||
|                     ); |                     )); | ||||||
|                 } else { |                 } else { | ||||||
|                     args.push("--repo".to_string()); |                     args.push("--repo".to_string()); | ||||||
|                     args.push(r.to_string()); |                     args.push(r.to_string()); | ||||||
| @ -183,9 +193,12 @@ impl HelmChart { | |||||||
|             None => args.push(self.name), |             None => args.push(self.name), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         if let Some(v) = self.version { |         match self.version { | ||||||
|             args.push("--version".to_string()); |             Some(v) => { | ||||||
|             args.push(v.to_string()); |                 args.push("--version".to_string()); | ||||||
|  |                 args.push(v.to_string()); | ||||||
|  |             } | ||||||
|  |             None => (), | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         args |         args | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||||
| 
 | 
 | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use log::debug; | use log::{debug, info}; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|  | |||||||
| @ -135,8 +135,6 @@ impl<T: Topology + K8sclient + HelmCommand> Interpret<T> for LAMPInterpret { | |||||||
| 
 | 
 | ||||||
|         info!("LAMP deployment_score {deployment_score:?}"); |         info!("LAMP deployment_score {deployment_score:?}"); | ||||||
| 
 | 
 | ||||||
|         let ingress_path = ingress_path!("/"); |  | ||||||
| 
 |  | ||||||
|         let lamp_ingress = K8sIngressScore { |         let lamp_ingress = K8sIngressScore { | ||||||
|             name: fqdn!("lamp-ingress"), |             name: fqdn!("lamp-ingress"), | ||||||
|             host: fqdn!("test"), |             host: fqdn!("test"), | ||||||
| @ -146,7 +144,7 @@ impl<T: Topology + K8sclient + HelmCommand> Interpret<T> for LAMPInterpret { | |||||||
|                     .as_str() |                     .as_str() | ||||||
|             ), |             ), | ||||||
|             port: 8080, |             port: 8080, | ||||||
|             path: Some(ingress_path), |             path: Some(ingress_path!("/")), | ||||||
|             path_type: None, |             path_type: None, | ||||||
|             namespace: self |             namespace: self | ||||||
|                 .get_namespace() |                 .get_namespace() | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ use crate::{ | |||||||
| #[async_trait] | #[async_trait] | ||||||
| impl AlertRule<KubePrometheus> for AlertManagerRuleGroup { | impl AlertRule<KubePrometheus> for AlertManagerRuleGroup { | ||||||
|     async fn install(&self, sender: &KubePrometheus) -> Result<Outcome, InterpretError> { |     async fn install(&self, sender: &KubePrometheus) -> Result<Outcome, InterpretError> { | ||||||
|         sender.install_rule(self).await |         sender.install_rule(&self).await | ||||||
|     } |     } | ||||||
|     fn clone_box(&self) -> Box<dyn AlertRule<KubePrometheus>> { |     fn clone_box(&self) -> Box<dyn AlertRule<KubePrometheus>> { | ||||||
|         Box::new(self.clone()) |         Box::new(self.clone()) | ||||||
| @ -28,7 +28,7 @@ impl AlertRule<KubePrometheus> for AlertManagerRuleGroup { | |||||||
| #[async_trait] | #[async_trait] | ||||||
| impl AlertRule<Prometheus> for AlertManagerRuleGroup { | impl AlertRule<Prometheus> for AlertManagerRuleGroup { | ||||||
|     async fn install(&self, sender: &Prometheus) -> Result<Outcome, InterpretError> { |     async fn install(&self, sender: &Prometheus) -> Result<Outcome, InterpretError> { | ||||||
|         sender.install_rule(self).await |         sender.install_rule(&self).await | ||||||
|     } |     } | ||||||
|     fn clone_box(&self) -> Box<dyn AlertRule<Prometheus>> { |     fn clone_box(&self) -> Box<dyn AlertRule<Prometheus>> { | ||||||
|         Box::new(self.clone()) |         Box::new(self.clone()) | ||||||
|  | |||||||
| @ -4,14 +4,15 @@ use std::str::FromStr; | |||||||
| use crate::modules::helm::chart::HelmChartScore; | use crate::modules::helm::chart::HelmChartScore; | ||||||
| 
 | 
 | ||||||
| pub fn grafana_helm_chart_score(ns: &str) -> HelmChartScore { | pub fn grafana_helm_chart_score(ns: &str) -> HelmChartScore { | ||||||
|     let values = r#" |     let values = format!( | ||||||
|  |         r#" | ||||||
| rbac: | rbac: | ||||||
|   namespaced: true |   namespaced: true | ||||||
| sidecar: | sidecar: | ||||||
|   dashboards: |   dashboards: | ||||||
|     enabled: true |     enabled: true | ||||||
|         "#
 |         "#
 | ||||||
|     .to_string(); |     ); | ||||||
| 
 | 
 | ||||||
|     HelmChartScore { |     HelmChartScore { | ||||||
|         namespace: Some(NonBlankString::from_str(ns).unwrap()), |         namespace: Some(NonBlankString::from_str(ns).unwrap()), | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| use kube::CustomResource; | use kube::CustomResource; | ||||||
| use schemars::JsonSchema; | use schemars::JsonSchema; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
|  | use std::collections::BTreeMap; | ||||||
| 
 | 
 | ||||||
| use super::crd_prometheuses::LabelSelector; | use super::crd_prometheuses::LabelSelector; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,13 @@ | |||||||
| use crate::modules::prometheus::alerts::k8s::{ | use std::collections::BTreeMap; | ||||||
|     deployment::alert_deployment_unavailable, | 
 | ||||||
|     pod::{alert_container_restarting, alert_pod_not_ready, pod_failed}, | use crate::modules::{ | ||||||
|     pvc::high_pvc_fill_rate_over_two_days, |     monitoring::alert_rule::prometheus_alert_rule::PrometheusAlertRule, | ||||||
|     service::alert_service_down, |     prometheus::alerts::k8s::{ | ||||||
|  |         deployment::alert_deployment_unavailable, | ||||||
|  |         pod::{alert_container_restarting, alert_pod_not_ready, pod_failed}, | ||||||
|  |         pvc::high_pvc_fill_rate_over_two_days, | ||||||
|  |         service::alert_service_down, | ||||||
|  |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::crd_prometheus_rules::Rule; | use super::crd_prometheus_rules::Rule; | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ use serde::{Deserialize, Serialize}; | |||||||
| 
 | 
 | ||||||
| use crate::modules::monitoring::alert_rule::prometheus_alert_rule::PrometheusAlertRule; | use crate::modules::monitoring::alert_rule::prometheus_alert_rule::PrometheusAlertRule; | ||||||
| 
 | 
 | ||||||
|  | use super::crd_default_rules::build_default_application_rules; | ||||||
|  | 
 | ||||||
| #[derive(CustomResource, Debug, Serialize, Deserialize, Clone, JsonSchema)] | #[derive(CustomResource, Debug, Serialize, Deserialize, Clone, JsonSchema)] | ||||||
| #[kube(
 | #[kube(
 | ||||||
|     group = "monitoring.coreos.com", |     group = "monitoring.coreos.com", | ||||||
|  | |||||||
| @ -1,9 +1,11 @@ | |||||||
| use std::collections::HashMap; | use std::collections::{BTreeMap, HashMap}; | ||||||
| 
 | 
 | ||||||
| use kube::CustomResource; | use kube::{CustomResource, Resource, api::ObjectMeta}; | ||||||
| use schemars::JsonSchema; | use schemars::JsonSchema; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
|  | use crate::interpret::InterpretError; | ||||||
|  | 
 | ||||||
| use crate::modules::monitoring::kube_prometheus::types::{ | use crate::modules::monitoring::kube_prometheus::types::{ | ||||||
|     HTTPScheme, MatchExpression, NamespaceSelector, Operator, Selector, |     HTTPScheme, MatchExpression, NamespaceSelector, Operator, Selector, | ||||||
|     ServiceMonitor as KubeServiceMonitor, ServiceMonitorEndpoint, |     ServiceMonitor as KubeServiceMonitor, ServiceMonitorEndpoint, | ||||||
| @ -48,7 +50,7 @@ pub struct ServiceMonitorSpec { | |||||||
| 
 | 
 | ||||||
| impl Default for ServiceMonitorSpec { | impl Default for ServiceMonitorSpec { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         let labels = HashMap::new(); |         let mut labels = HashMap::new(); | ||||||
|         Self { |         Self { | ||||||
|             selector: Selector { |             selector: Selector { | ||||||
|                 match_labels: { labels }, |                 match_labels: { labels }, | ||||||
|  | |||||||
| @ -27,12 +27,6 @@ pub struct KubePrometheusConfig { | |||||||
|     pub alert_rules: Vec<AlertManagerAdditionalPromRules>, |     pub alert_rules: Vec<AlertManagerAdditionalPromRules>, | ||||||
|     pub additional_service_monitors: Vec<ServiceMonitor>, |     pub additional_service_monitors: Vec<ServiceMonitor>, | ||||||
| } | } | ||||||
| impl Default for KubePrometheusConfig { |  | ||||||
|     fn default() -> Self { |  | ||||||
|         Self::new() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl KubePrometheusConfig { | impl KubePrometheusConfig { | ||||||
|     pub fn new() -> Self { |     pub fn new() -> Self { | ||||||
|         Self { |         Self { | ||||||
|  | |||||||
| @ -35,7 +35,7 @@ pub fn kube_prometheus_helm_chart_score( | |||||||
|     let kube_proxy = config.kube_proxy.to_string(); |     let kube_proxy = config.kube_proxy.to_string(); | ||||||
|     let kube_state_metrics = config.kube_state_metrics.to_string(); |     let kube_state_metrics = config.kube_state_metrics.to_string(); | ||||||
|     let node_exporter = config.node_exporter.to_string(); |     let node_exporter = config.node_exporter.to_string(); | ||||||
|     let _prometheus_operator = config.prometheus_operator.to_string(); |     let prometheus_operator = config.prometheus_operator.to_string(); | ||||||
|     let prometheus = config.prometheus.to_string(); |     let prometheus = config.prometheus.to_string(); | ||||||
|     let resource_limit = Resources { |     let resource_limit = Resources { | ||||||
|         limits: Limits { |         limits: Limits { | ||||||
| @ -64,7 +64,7 @@ pub fn kube_prometheus_helm_chart_score( | |||||||
|             indent_lines(&yaml, indent_level + 2) |             indent_lines(&yaml, indent_level + 2) | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|     let _resource_section = resource_block(&resource_limit, 2); |     let resource_section = resource_block(&resource_limit, 2); | ||||||
| 
 | 
 | ||||||
|     let mut values = format!( |     let mut values = format!( | ||||||
|         r#" |         r#" | ||||||
|  | |||||||
| @ -55,12 +55,6 @@ pub struct KubePrometheus { | |||||||
|     pub config: Arc<Mutex<KubePrometheusConfig>>, |     pub config: Arc<Mutex<KubePrometheusConfig>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Default for KubePrometheus { |  | ||||||
|     fn default() -> Self { |  | ||||||
|         Self::new() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl KubePrometheus { | impl KubePrometheus { | ||||||
|     pub fn new() -> Self { |     pub fn new() -> Self { | ||||||
|         Self { |         Self { | ||||||
|  | |||||||
| @ -1,3 +1,2 @@ | |||||||
| pub mod helm; | pub mod helm; | ||||||
| #[allow(clippy::module_inception)] |  | ||||||
| pub mod ntfy; | pub mod ntfy; | ||||||
|  | |||||||
| @ -28,7 +28,7 @@ impl<T: Topology + HelmCommand + K8sclient> Score<T> for NtfyScore { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn name(&self) -> String { |     fn name(&self) -> String { | ||||||
|         "Ntfy".to_string() |         format!("Ntfy") | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -39,21 +39,31 @@ pub struct NtfyInterpret { | |||||||
| 
 | 
 | ||||||
| #[derive(Debug, EnumString, Display)] | #[derive(Debug, EnumString, Display)] | ||||||
| enum NtfyAccessMode { | enum NtfyAccessMode { | ||||||
|     #[strum(serialize = "read-write", serialize = "rw")] |     #[strum(serialize = "read-write", serialize = "rw", to_string = "read-write")] | ||||||
|     ReadWrite, |     ReadWrite, | ||||||
|     #[strum(serialize = "read-only", serialize = "ro", serialize = "read")] |     #[strum(
 | ||||||
|  |         serialize = "read-only", | ||||||
|  |         serialize = "ro", | ||||||
|  |         serialize = "read", | ||||||
|  |         to_string = "read-only" | ||||||
|  |     )] | ||||||
|     ReadOnly, |     ReadOnly, | ||||||
|     #[strum(serialize = "write-only", serialize = "wo", serialize = "write")] |     #[strum(
 | ||||||
|  |         serialize = "write-only", | ||||||
|  |         serialize = "wo", | ||||||
|  |         serialize = "write", | ||||||
|  |         to_string = "write-only" | ||||||
|  |     )] | ||||||
|     WriteOnly, |     WriteOnly, | ||||||
|     #[strum(serialize = "deny", serialize = "none")] |     #[strum(serialize = "none", to_string = "deny")] | ||||||
|     Deny, |     Deny, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, EnumString, Display)] | #[derive(Debug, EnumString, Display)] | ||||||
| enum NtfyRole { | enum NtfyRole { | ||||||
|     #[strum(serialize = "user")] |     #[strum(serialize = "user", to_string = "user")] | ||||||
|     User, |     User, | ||||||
|     #[strum(serialize = "admin")] |     #[strum(serialize = "admin", to_string = "admin")] | ||||||
|     Admin, |     Admin, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -85,6 +95,28 @@ impl NtfyInterpret { | |||||||
| 
 | 
 | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     async fn set_access( | ||||||
|  |         &self, | ||||||
|  |         k8s_client: Arc<K8sClient>, | ||||||
|  |         username: &str, | ||||||
|  |         topic: &str, | ||||||
|  |         mode: NtfyAccessMode, | ||||||
|  |     ) -> Result<(), String> { | ||||||
|  |         k8s_client | ||||||
|  |             .exec_app( | ||||||
|  |                 "ntfy".to_string(), | ||||||
|  |                 Some(&self.score.namespace), | ||||||
|  |                 vec![ | ||||||
|  |                     "sh", | ||||||
|  |                     "-c", | ||||||
|  |                     format!("ntfy access {username} {topic} {mode}").as_str(), | ||||||
|  |                 ], | ||||||
|  |             ) | ||||||
|  |             .await?; | ||||||
|  | 
 | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// We need a ntfy interpret to wrap the HelmChartScore in order to run the score, and then bootstrap the config inside ntfy
 | /// We need a ntfy interpret to wrap the HelmChartScore in order to run the score, and then bootstrap the config inside ntfy
 | ||||||
| @ -109,7 +141,7 @@ impl<T: Topology + HelmCommand + K8sclient> Interpret<T> for NtfyInterpret { | |||||||
|         client |         client | ||||||
|             .wait_until_deployment_ready( |             .wait_until_deployment_ready( | ||||||
|                 "ntfy".to_string(), |                 "ntfy".to_string(), | ||||||
|                 Some(self.score.namespace.as_str()), |                 Some(&self.score.namespace.as_str()), | ||||||
|                 None, |                 None, | ||||||
|             ) |             ) | ||||||
|             .await?; |             .await?; | ||||||
|  | |||||||
| @ -1,4 +1,3 @@ | |||||||
| pub mod helm; | pub mod helm; | ||||||
| #[allow(clippy::module_inception)] |  | ||||||
| pub mod prometheus; | pub mod prometheus; | ||||||
| pub mod prometheus_config; | pub mod prometheus_config; | ||||||
|  | |||||||
| @ -37,12 +37,6 @@ impl AlertSender for Prometheus { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Default for Prometheus { |  | ||||||
|     fn default() -> Self { |  | ||||||
|         Self::new() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Prometheus { | impl Prometheus { | ||||||
|     pub fn new() -> Self { |     pub fn new() -> Self { | ||||||
|         Self { |         Self { | ||||||
| @ -120,9 +114,9 @@ impl Prometheus { | |||||||
|                 .execute(inventory, topology) |                 .execute(inventory, topology) | ||||||
|                 .await |                 .await | ||||||
|         } else { |         } else { | ||||||
|             Err(InterpretError::new( |             Err(InterpretError::new(format!( | ||||||
|                 "could not install grafana, missing namespace".to_string(), |                 "could not install grafana, missing namespace", | ||||||
|             )) |             ))) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,12 +16,6 @@ pub struct PrometheusConfig { | |||||||
|     pub additional_service_monitors: Vec<ServiceMonitor>, |     pub additional_service_monitors: Vec<ServiceMonitor>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Default for PrometheusConfig { |  | ||||||
|     fn default() -> Self { |  | ||||||
|         Self::new() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl PrometheusConfig { | impl PrometheusConfig { | ||||||
|     pub fn new() -> Self { |     pub fn new() -> Self { | ||||||
|         Self { |         Self { | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ impl OKDBootstrapDhcpScore { | |||||||
|             logical_host: topology.bootstrap_host.clone(), |             logical_host: topology.bootstrap_host.clone(), | ||||||
|             physical_host: inventory |             physical_host: inventory | ||||||
|                 .worker_host |                 .worker_host | ||||||
|                 .first() |                 .get(0) | ||||||
|                 .expect("Should have at least one worker to be used as bootstrap node") |                 .expect("Should have at least one worker to be used as bootstrap node") | ||||||
|                 .clone(), |                 .clone(), | ||||||
|         }); |         }); | ||||||
|  | |||||||
| @ -6,12 +6,6 @@ pub struct OKDUpgradeScore { | |||||||
|     _target_version: Version, |     _target_version: Version, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Default for OKDUpgradeScore { |  | ||||||
|     fn default() -> Self { |  | ||||||
|         Self::new() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl OKDUpgradeScore { | impl OKDUpgradeScore { | ||||||
|     pub fn new() -> Self { |     pub fn new() -> Self { | ||||||
|         Self { |         Self { | ||||||
|  | |||||||
| @ -93,9 +93,9 @@ impl<T: Topology + K8sclient + PrometheusApplicationMonitoring<CRDPrometheus>> I | |||||||
|         self.install_rules(&self.prometheus_rules, &client).await?; |         self.install_rules(&self.prometheus_rules, &client).await?; | ||||||
|         self.install_monitors(self.service_monitors.clone(), &client) |         self.install_monitors(self.service_monitors.clone(), &client) | ||||||
|             .await?; |             .await?; | ||||||
|         Ok(Outcome::success( |         Ok(Outcome::success(format!( | ||||||
|             "deployed application monitoring composants".to_string(), |             "deployed application monitoring composants" | ||||||
|         )) |         ))) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn get_name(&self) -> InterpretName { |     fn get_name(&self) -> InterpretName { | ||||||
| @ -415,7 +415,7 @@ impl K8sPrometheusCRDAlertingInterpret { | |||||||
| 
 | 
 | ||||||
|     async fn install_rules( |     async fn install_rules( | ||||||
|         &self, |         &self, | ||||||
|         #[allow(clippy::ptr_arg)] rules: &Vec<RuleGroup>, |         rules: &Vec<RuleGroup>, | ||||||
|         client: &Arc<K8sClient>, |         client: &Arc<K8sClient>, | ||||||
|     ) -> Result<Outcome, InterpretError> { |     ) -> Result<Outcome, InterpretError> { | ||||||
|         let mut prom_rule_spec = PrometheusRuleSpec { |         let mut prom_rule_spec = PrometheusRuleSpec { | ||||||
| @ -423,7 +423,7 @@ impl K8sPrometheusCRDAlertingInterpret { | |||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         let default_rules_group = RuleGroup { |         let default_rules_group = RuleGroup { | ||||||
|             name: "default-rules".to_string(), |             name: format!("default-rules"), | ||||||
|             rules: build_default_application_rules(), |             rules: build_default_application_rules(), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,3 @@ | |||||||
| pub mod alerts; | pub mod alerts; | ||||||
| pub mod k8s_prometheus_alerting_score; | pub mod k8s_prometheus_alerting_score; | ||||||
| #[allow(clippy::module_inception)] |  | ||||||
| pub mod prometheus; | pub mod prometheus; | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ use harmony::instrumentation::{self, HarmonyEvent}; | |||||||
| use indicatif::{MultiProgress, ProgressBar}; | use indicatif::{MultiProgress, ProgressBar}; | ||||||
| use indicatif_log_bridge::LogWrapper; | use indicatif_log_bridge::LogWrapper; | ||||||
| use std::{ | use std::{ | ||||||
|     collections::HashMap, |     collections::{HashMap, hash_map}, | ||||||
|     sync::{Arc, Mutex}, |     sync::{Arc, Mutex}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -11,6 +11,8 @@ pub mod progress; | |||||||
| pub mod theme; | pub mod theme; | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "tui")] | #[cfg(feature = "tui")] | ||||||
|  | use harmony_tui; | ||||||
|  | 
 | ||||||
| #[derive(Parser, Debug)] | #[derive(Parser, Debug)] | ||||||
| #[command(version, about, long_about = None)] | #[command(version, about, long_about = None)] | ||||||
| pub struct Args { | pub struct Args { | ||||||
| @ -71,7 +73,7 @@ fn maestro_scores_filter<T: Topology>( | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     scores_vec |     return scores_vec; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO: consider adding doctest for this function
 | // TODO: consider adding doctest for this function
 | ||||||
| @ -81,7 +83,7 @@ fn list_scores_with_index<T: Topology>(scores_vec: &Vec<Box<dyn Score<T>>>) -> S | |||||||
|         let name = s.name(); |         let name = s.name(); | ||||||
|         display_str.push_str(&format!("\n{i}: {name}")); |         display_str.push_str(&format!("\n{i}: {name}")); | ||||||
|     } |     } | ||||||
|     display_str |     return display_str; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn run<T: Topology + Send + Sync + 'static>( | pub async fn run<T: Topology + Send + Sync + 'static>( | ||||||
| @ -124,7 +126,7 @@ async fn init<T: Topology + Send + Sync + 'static>( | |||||||
| 
 | 
 | ||||||
|     let scores_vec = maestro_scores_filter(&maestro, args.all, args.filter, args.number); |     let scores_vec = maestro_scores_filter(&maestro, args.all, args.filter, args.number); | ||||||
| 
 | 
 | ||||||
|     if scores_vec.is_empty() { |     if scores_vec.len() == 0 { | ||||||
|         return Err("No score found".into()); |         return Err("No score found".into()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -263,7 +265,7 @@ mod test { | |||||||
| 
 | 
 | ||||||
|         assert!( |         assert!( | ||||||
|             maestro |             maestro | ||||||
|                 .interpret(res.first().unwrap().clone_box()) |                 .interpret(res.get(0).unwrap().clone_box()) | ||||||
|                 .await |                 .await | ||||||
|                 .is_ok() |                 .is_ok() | ||||||
|         ); |         ); | ||||||
| @ -279,7 +281,7 @@ mod test { | |||||||
| 
 | 
 | ||||||
|         assert!( |         assert!( | ||||||
|             maestro |             maestro | ||||||
|                 .interpret(res.first().unwrap().clone_box()) |                 .interpret(res.get(0).unwrap().clone_box()) | ||||||
|                 .await |                 .await | ||||||
|                 .is_err() |                 .is_err() | ||||||
|         ); |         ); | ||||||
| @ -295,7 +297,7 @@ mod test { | |||||||
| 
 | 
 | ||||||
|         assert!( |         assert!( | ||||||
|             maestro |             maestro | ||||||
|                 .interpret(res.first().unwrap().clone_box()) |                 .interpret(res.get(0).unwrap().clone_box()) | ||||||
|                 .await |                 .await | ||||||
|                 .is_ok() |                 .is_ok() | ||||||
|         ); |         ); | ||||||
| @ -317,7 +319,7 @@ mod test { | |||||||
| 
 | 
 | ||||||
|         assert!( |         assert!( | ||||||
|             maestro |             maestro | ||||||
|                 .interpret(res.first().unwrap().clone_box()) |                 .interpret(res.get(0).unwrap().clone_box()) | ||||||
|                 .await |                 .await | ||||||
|                 .is_ok() |                 .is_ok() | ||||||
|         ); |         ); | ||||||
| @ -329,6 +331,6 @@ mod test { | |||||||
| 
 | 
 | ||||||
|         let res = crate::maestro_scores_filter(&maestro, false, None, 11); |         let res = crate::maestro_scores_filter(&maestro, false, None, 11); | ||||||
| 
 | 
 | ||||||
|         assert!(res.is_empty()); |         assert!(res.len() == 0); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -95,7 +95,7 @@ pub async fn handle_events() { | |||||||
|                         )); |                         )); | ||||||
|                         (*progresses_guard).insert(PROGRESS_DEPLOYMENT.to_string(), multi_progress); |                         (*progresses_guard).insert(PROGRESS_DEPLOYMENT.to_string(), multi_progress); | ||||||
|                     } |                     } | ||||||
|                     HarmonyComposerEvent::DeploymentCompleted => println!("\n"), |                     HarmonyComposerEvent::DeploymentCompleted { details } => println!("\n"), | ||||||
|                     HarmonyComposerEvent::Shutdown => { |                     HarmonyComposerEvent::Shutdown => { | ||||||
|                         for (_, progresses) in (*progresses_guard).iter() { |                         for (_, progresses) in (*progresses_guard).iter() { | ||||||
|                             progresses.clear().unwrap(); |                             progresses.clear().unwrap(); | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ pub enum HarmonyComposerEvent { | |||||||
|     ProjectCompiled, |     ProjectCompiled, | ||||||
|     ProjectCompilationFailed { details: String }, |     ProjectCompilationFailed { details: String }, | ||||||
|     DeploymentStarted { target: String }, |     DeploymentStarted { target: String }, | ||||||
|     DeploymentCompleted, |     DeploymentCompleted { details: String }, | ||||||
|     Shutdown, |     Shutdown, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -80,13 +80,14 @@ async fn main() { | |||||||
|     instrumentation::instrument(HarmonyComposerEvent::ProjectInitializationStarted).unwrap(); |     instrumentation::instrument(HarmonyComposerEvent::ProjectInitializationStarted).unwrap(); | ||||||
| 
 | 
 | ||||||
|     let harmony_bin_path: PathBuf = match harmony_path { |     let harmony_bin_path: PathBuf = match harmony_path { | ||||||
|         true => compile_harmony( |         true => { | ||||||
|             cli_args.compile_method, |             compile_harmony( | ||||||
|             cli_args.compile_platform, |                 cli_args.compile_method, | ||||||
|             cli_args.harmony_path.clone(), |                 cli_args.compile_platform, | ||||||
|         ) |                 cli_args.harmony_path.clone(), | ||||||
|         .await |             ) | ||||||
|         .expect("couldn't compile harmony"), |             .await | ||||||
|  |         } | ||||||
|         false => todo!("implement autodetect code"), |         false => todo!("implement autodetect code"), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| @ -144,9 +145,10 @@ async fn main() { | |||||||
|                 .expect("failed to run harmony deploy"); |                 .expect("failed to run harmony deploy"); | ||||||
| 
 | 
 | ||||||
|                 let deploy_output = deploy.wait_with_output().unwrap(); |                 let deploy_output = deploy.wait_with_output().unwrap(); | ||||||
|                 debug!("{}", String::from_utf8(deploy_output.stdout).unwrap()); |                 instrumentation::instrument(HarmonyComposerEvent::DeploymentCompleted { | ||||||
| 
 |                     details: String::from_utf8(deploy_output.stdout).unwrap(), | ||||||
|                 instrumentation::instrument(HarmonyComposerEvent::DeploymentCompleted).unwrap(); |                 }) | ||||||
|  |                 .unwrap(); | ||||||
|             } |             } | ||||||
|             Commands::All(_args) => todo!( |             Commands::All(_args) => todo!( | ||||||
|                 "take all previous match arms and turn them into separate functions, and call them all one after the other" |                 "take all previous match arms and turn them into separate functions, and call them all one after the other" | ||||||
| @ -171,7 +173,7 @@ async fn compile_harmony( | |||||||
|     method: Option<CompileMethod>, |     method: Option<CompileMethod>, | ||||||
|     platform: Option<String>, |     platform: Option<String>, | ||||||
|     harmony_location: String, |     harmony_location: String, | ||||||
| ) -> Result<PathBuf, String> { | ) -> PathBuf { | ||||||
|     let platform = match platform { |     let platform = match platform { | ||||||
|         Some(p) => p, |         Some(p) => p, | ||||||
|         None => current_platform::CURRENT_PLATFORM.to_string(), |         None => current_platform::CURRENT_PLATFORM.to_string(), | ||||||
| @ -201,7 +203,6 @@ async fn compile_harmony( | |||||||
|                 details: "compiling project with cargo".to_string(), |                 details: "compiling project with cargo".to_string(), | ||||||
|             }) |             }) | ||||||
|             .unwrap(); |             .unwrap(); | ||||||
| 
 |  | ||||||
|             compile_cargo(platform, harmony_location).await |             compile_cargo(platform, harmony_location).await | ||||||
|         } |         } | ||||||
|         CompileMethod::Docker => { |         CompileMethod::Docker => { | ||||||
| @ -209,28 +210,16 @@ async fn compile_harmony( | |||||||
|                 details: "compiling project with docker".to_string(), |                 details: "compiling project with docker".to_string(), | ||||||
|             }) |             }) | ||||||
|             .unwrap(); |             .unwrap(); | ||||||
| 
 |  | ||||||
|             compile_docker(platform, harmony_location).await |             compile_docker(platform, harmony_location).await | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     match path { |     instrumentation::instrument(HarmonyComposerEvent::ProjectCompiled).unwrap(); | ||||||
|         Ok(path) => { |     path | ||||||
|             instrumentation::instrument(HarmonyComposerEvent::ProjectCompiled).unwrap(); |  | ||||||
|             Ok(path) |  | ||||||
|         } |  | ||||||
|         Err(err) => { |  | ||||||
|             instrumentation::instrument(HarmonyComposerEvent::ProjectCompilationFailed { |  | ||||||
|                 details: err.clone(), |  | ||||||
|             }) |  | ||||||
|             .unwrap(); |  | ||||||
|             Err(err) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO: make sure this works with cargo workspaces
 | // TODO: make sure this works with cargo workspaces
 | ||||||
| async fn compile_cargo(platform: String, harmony_location: String) -> Result<PathBuf, String> { | async fn compile_cargo(platform: String, harmony_location: String) -> PathBuf { | ||||||
|     let metadata = MetadataCommand::new() |     let metadata = MetadataCommand::new() | ||||||
|         .manifest_path(format!("{}/Cargo.toml", harmony_location)) |         .manifest_path(format!("{}/Cargo.toml", harmony_location)) | ||||||
|         .exec() |         .exec() | ||||||
| @ -279,10 +268,7 @@ async fn compile_cargo(platform: String, harmony_location: String) -> Result<Pat | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let res = cargo_build.wait(); //.expect("run cargo command failed");
 |     cargo_build.wait().expect("run cargo command failed"); | ||||||
|     if res.is_err() { |  | ||||||
|         return Err("cargo build failed".into()); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     let bin = artifacts |     let bin = artifacts | ||||||
|         .last() |         .last() | ||||||
| @ -300,10 +286,10 @@ async fn compile_cargo(platform: String, harmony_location: String) -> Result<Pat | |||||||
|         let _copy_res = fs::copy(&bin, &bin_out).await; |         let _copy_res = fs::copy(&bin, &bin_out).await; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Ok(bin_out) |     bin_out | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async fn compile_docker(platform: String, harmony_location: String) -> Result<PathBuf, String> { | async fn compile_docker(platform: String, harmony_location: String) -> PathBuf { | ||||||
|     let docker_client = |     let docker_client = | ||||||
|         bollard::Docker::connect_with_local_defaults().expect("couldn't connect to docker"); |         bollard::Docker::connect_with_local_defaults().expect("couldn't connect to docker"); | ||||||
| 
 | 
 | ||||||
| @ -319,7 +305,7 @@ async fn compile_docker(platform: String, harmony_location: String) -> Result<Pa | |||||||
|         .await |         .await | ||||||
|         .expect("list containers failed"); |         .expect("list containers failed"); | ||||||
| 
 | 
 | ||||||
|     if !containers.is_empty() { |     if containers.len() > 0 { | ||||||
|         docker_client |         docker_client | ||||||
|             .remove_container("harmony_build", None::<RemoveContainerOptions>) |             .remove_container("harmony_build", None::<RemoveContainerOptions>) | ||||||
|             .await |             .await | ||||||
| @ -381,12 +367,12 @@ async fn compile_docker(platform: String, harmony_location: String) -> Result<Pa | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // wait until container is no longer running
 |     // wait until container is no longer running
 | ||||||
|     while (wait.next().await).is_some() {} |     while let Some(_) = wait.next().await {} | ||||||
| 
 | 
 | ||||||
|     // hack that should be cleaned up
 |     // hack that should be cleaned up
 | ||||||
|     if platform.contains("windows") { |     if platform.contains("windows") { | ||||||
|         Ok(PathBuf::from(format!("{}/harmony.exe", harmony_location))) |         return PathBuf::from(format!("{}/harmony.exe", harmony_location)); | ||||||
|     } else { |     } else { | ||||||
|         Ok(PathBuf::from(format!("{}/harmony", harmony_location))) |         return PathBuf::from(format!("{}/harmony", harmony_location)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -11,13 +11,13 @@ pub fn ip(input: TokenStream) -> TokenStream { | |||||||
|     let input = parse_macro_input!(input as LitStr); |     let input = parse_macro_input!(input as LitStr); | ||||||
|     let ip_str = input.value(); |     let ip_str = input.value(); | ||||||
| 
 | 
 | ||||||
|     if ip_str.parse::<std::net::Ipv4Addr>().is_ok() { |     if let Ok(_) = ip_str.parse::<std::net::Ipv4Addr>() { | ||||||
|         let expanded = |         let expanded = | ||||||
|             quote! { std::net::IpAddr::V4(#ip_str.parse::<std::net::Ipv4Addr>().unwrap()) }; |             quote! { std::net::IpAddr::V4(#ip_str.parse::<std::net::Ipv4Addr>().unwrap()) }; | ||||||
|         return TokenStream::from(expanded); |         return TokenStream::from(expanded); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if ip_str.parse::<std::net::Ipv6Addr>().is_ok() { |     if let Ok(_) = ip_str.parse::<std::net::Ipv6Addr>() { | ||||||
|         let expanded = |         let expanded = | ||||||
|             quote! { std::net::IpAddr::V6(#ip_str.parse::<std::net::Ipv6Addr>().unwrap()) }; |             quote! { std::net::IpAddr::V6(#ip_str.parse::<std::net::Ipv6Addr>().unwrap()) }; | ||||||
|         return TokenStream::from(expanded); |         return TokenStream::from(expanded); | ||||||
| @ -31,7 +31,7 @@ pub fn ipv4(input: TokenStream) -> TokenStream { | |||||||
|     let input = parse_macro_input!(input as LitStr); |     let input = parse_macro_input!(input as LitStr); | ||||||
|     let ip_str = input.value(); |     let ip_str = input.value(); | ||||||
| 
 | 
 | ||||||
|     if ip_str.parse::<std::net::Ipv4Addr>().is_ok() { |     if let Ok(_) = ip_str.parse::<std::net::Ipv4Addr>() { | ||||||
|         let expanded = quote! { #ip_str.parse::<std::net::Ipv4Addr>().unwrap() }; |         let expanded = quote! { #ip_str.parse::<std::net::Ipv4Addr>().unwrap() }; | ||||||
|         return TokenStream::from(expanded); |         return TokenStream::from(expanded); | ||||||
|     } |     } | ||||||
| @ -127,7 +127,7 @@ pub fn ingress_path(input: TokenStream) -> TokenStream { | |||||||
|     match path_str.starts_with("/") { |     match path_str.starts_with("/") { | ||||||
|         true => { |         true => { | ||||||
|             let expanded = quote! {(#path_str.to_string()) }; |             let expanded = quote! {(#path_str.to_string()) }; | ||||||
|             TokenStream::from(expanded) |             return TokenStream::from(expanded); | ||||||
|         } |         } | ||||||
|         false => panic!("Invalid ingress path"), |         false => panic!("Invalid ingress path"), | ||||||
|     } |     } | ||||||
| @ -138,7 +138,7 @@ pub fn cidrv4(input: TokenStream) -> TokenStream { | |||||||
|     let input = parse_macro_input!(input as LitStr); |     let input = parse_macro_input!(input as LitStr); | ||||||
|     let cidr_str = input.value(); |     let cidr_str = input.value(); | ||||||
| 
 | 
 | ||||||
|     if cidr_str.parse::<cidr::Ipv4Cidr>().is_ok() { |     if let Ok(_) = cidr_str.parse::<cidr::Ipv4Cidr>() { | ||||||
|         let expanded = quote! { #cidr_str.parse::<cidr::Ipv4Cidr>().unwrap() }; |         let expanded = quote! { #cidr_str.parse::<cidr::Ipv4Cidr>().unwrap() }; | ||||||
|         return TokenStream::from(expanded); |         return TokenStream::from(expanded); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -14,9 +14,9 @@ use tokio::sync::mpsc; | |||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| enum ExecutionState { | enum ExecutionState { | ||||||
|     Initiated, |     INITIATED, | ||||||
|     Running, |     RUNNING, | ||||||
|     Canceled, |     CANCELED, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct Execution<T: Topology> { | struct Execution<T: Topology> { | ||||||
| @ -62,7 +62,7 @@ impl<T: Topology> ScoreListWidget<T> { | |||||||
|     pub(crate) fn launch_execution(&mut self) { |     pub(crate) fn launch_execution(&mut self) { | ||||||
|         if let Some(score) = self.get_selected_score() { |         if let Some(score) = self.get_selected_score() { | ||||||
|             self.execution = Some(Execution { |             self.execution = Some(Execution { | ||||||
|                 state: ExecutionState::Initiated, |                 state: ExecutionState::INITIATED, | ||||||
|                 score: score.clone_box(), |                 score: score.clone_box(), | ||||||
|             }); |             }); | ||||||
|             info!("{}\n\nConfirm Execution (Press y/n)", score.name()); |             info!("{}\n\nConfirm Execution (Press y/n)", score.name()); | ||||||
| @ -106,7 +106,7 @@ impl<T: Topology> ScoreListWidget<T> { | |||||||
|         if let Some(execution) = &mut self.execution { |         if let Some(execution) = &mut self.execution { | ||||||
|             match confirm { |             match confirm { | ||||||
|                 true => { |                 true => { | ||||||
|                     execution.state = ExecutionState::Running; |                     execution.state = ExecutionState::RUNNING; | ||||||
|                     info!("Launch execution {execution}"); |                     info!("Launch execution {execution}"); | ||||||
|                     self.sender |                     self.sender | ||||||
|                         .send(HarmonyTuiEvent::LaunchScore(execution.score.clone_box())) |                         .send(HarmonyTuiEvent::LaunchScore(execution.score.clone_box())) | ||||||
| @ -114,7 +114,7 @@ impl<T: Topology> ScoreListWidget<T> { | |||||||
|                         .expect("Should be able to send message"); |                         .expect("Should be able to send message"); | ||||||
|                 } |                 } | ||||||
|                 false => { |                 false => { | ||||||
|                     execution.state = ExecutionState::Canceled; |                     execution.state = ExecutionState::CANCELED; | ||||||
|                     info!("Execution cancelled"); |                     info!("Execution cancelled"); | ||||||
|                     self.clear_execution(); |                     self.clear_execution(); | ||||||
|                 } |                 } | ||||||
| @ -144,11 +144,7 @@ impl<T: Topology> Widget for &ScoreListWidget<T> { | |||||||
|         Self: Sized, |         Self: Sized, | ||||||
|     { |     { | ||||||
|         let mut list_state = self.list_state.write().unwrap(); |         let mut list_state = self.list_state.write().unwrap(); | ||||||
|         let scores_items: Vec<ListItem<'_>> = self |         let scores_items: Vec<ListItem<'_>> = self.scores.iter().map(score_to_list_item).collect(); | ||||||
|             .scores |  | ||||||
|             .iter() |  | ||||||
|             .map(|score| ListItem::new(score.name())) |  | ||||||
|             .collect(); |  | ||||||
|         let list = List::new(scores_items) |         let list = List::new(scores_items) | ||||||
|             .highlight_style(Style::new().bold().italic()) |             .highlight_style(Style::new().bold().italic()) | ||||||
|             .highlight_symbol("🠊 "); |             .highlight_symbol("🠊 "); | ||||||
| @ -156,3 +152,7 @@ impl<T: Topology> Widget for &ScoreListWidget<T> { | |||||||
|         StatefulWidget::render(list, area, buf, &mut list_state) |         StatefulWidget::render(list, area, buf, &mut list_state) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | fn score_to_list_item<'a, T: Topology>(score: &'a Box<dyn Score<T>>) -> ListItem<'a> { | ||||||
|  |     ListItem::new(score.name()) | ||||||
|  | } | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ mod downloadable_asset; | |||||||
| use downloadable_asset::*; | use downloadable_asset::*; | ||||||
| 
 | 
 | ||||||
| use kube::Client; | use kube::Client; | ||||||
| use log::debug; | use log::{debug, warn}; | ||||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||||
| 
 | 
 | ||||||
| const K3D_BIN_FILE_NAME: &str = "k3d"; | const K3D_BIN_FILE_NAME: &str = "k3d"; | ||||||
| @ -368,7 +368,7 @@ mod test { | |||||||
|     async fn k3d_latest_release_should_get_latest() { |     async fn k3d_latest_release_should_get_latest() { | ||||||
|         let dir = get_clean_test_directory(); |         let dir = get_clean_test_directory(); | ||||||
| 
 | 
 | ||||||
|         assert!(!dir.join(K3D_BIN_FILE_NAME).exists()); |         assert_eq!(dir.join(K3D_BIN_FILE_NAME).exists(), false); | ||||||
| 
 | 
 | ||||||
|         let k3d = K3d::new(dir.clone(), None); |         let k3d = K3d::new(dir.clone(), None); | ||||||
|         let latest_release = k3d.get_latest_release_tag().await.unwrap(); |         let latest_release = k3d.get_latest_release_tag().await.unwrap(); | ||||||
| @ -382,12 +382,12 @@ mod test { | |||||||
|     async fn k3d_download_latest_release_should_get_latest_bin() { |     async fn k3d_download_latest_release_should_get_latest_bin() { | ||||||
|         let dir = get_clean_test_directory(); |         let dir = get_clean_test_directory(); | ||||||
| 
 | 
 | ||||||
|         assert!(!dir.join(K3D_BIN_FILE_NAME).exists()); |         assert_eq!(dir.join(K3D_BIN_FILE_NAME).exists(), false); | ||||||
| 
 | 
 | ||||||
|         let k3d = K3d::new(dir.clone(), None); |         let k3d = K3d::new(dir.clone(), None); | ||||||
|         let bin_file_path = k3d.download_latest_release().await.unwrap(); |         let bin_file_path = k3d.download_latest_release().await.unwrap(); | ||||||
|         assert_eq!(bin_file_path, dir.join(K3D_BIN_FILE_NAME)); |         assert_eq!(bin_file_path, dir.join(K3D_BIN_FILE_NAME)); | ||||||
|         assert!(dir.join(K3D_BIN_FILE_NAME).exists()); |         assert_eq!(dir.join(K3D_BIN_FILE_NAME).exists(), true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn get_clean_test_directory() -> PathBuf { |     fn get_clean_test_directory() -> PathBuf { | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | use rand; | ||||||
| use rand::Rng; | use rand::Rng; | ||||||
| use xml::reader::XmlEvent as ReadEvent; | use xml::reader::XmlEvent as ReadEvent; | ||||||
| use xml::writer::XmlEvent as WriteEvent; | use xml::writer::XmlEvent as WriteEvent; | ||||||
| @ -13,7 +14,7 @@ impl YaDeserializeTrait for HAProxyId { | |||||||
|             ReadEvent::StartElement { |             ReadEvent::StartElement { | ||||||
|                 name, attributes, .. |                 name, attributes, .. | ||||||
|             } => { |             } => { | ||||||
|                 if !attributes.is_empty() { |                 if attributes.len() > 0 { | ||||||
|                     return Err(String::from( |                     return Err(String::from( | ||||||
|                         "Attributes not currently supported by HAProxyId", |                         "Attributes not currently supported by HAProxyId", | ||||||
|                     )); |                     )); | ||||||
|  | |||||||
| @ -51,7 +51,7 @@ pub struct OPNsense { | |||||||
| impl From<String> for OPNsense { | impl From<String> for OPNsense { | ||||||
|     fn from(content: String) -> Self { |     fn from(content: String) -> Self { | ||||||
|         yaserde::de::from_str(&content) |         yaserde::de::from_str(&content) | ||||||
|             .map_err(|e| println!("{}", e)) |             .map_err(|e| println!("{}", e.to_string())) | ||||||
|             .expect("OPNSense received invalid string, should be full XML") |             .expect("OPNSense received invalid string, should be full XML") | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -59,7 +59,7 @@ impl From<String> for OPNsense { | |||||||
| impl OPNsense { | impl OPNsense { | ||||||
|     pub fn to_xml(&self) -> String { |     pub fn to_xml(&self) -> String { | ||||||
|         to_xml_str(self) |         to_xml_str(self) | ||||||
|             .map_err(|e| error!("{}", e)) |             .map_err(|e| error!("{}", e.to_string())) | ||||||
|             .expect("OPNSense could not serialize to XML") |             .expect("OPNSense could not serialize to XML") | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ readme.workspace = true | |||||||
| license.workspace = true | license.workspace = true | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| serde = { version = "1.0.123", features = ["derive"] } | serde = { version = "1.0.123", features = [ "derive" ] } | ||||||
| log = { workspace = true } | log = { workspace = true } | ||||||
| env_logger = { workspace = true } | env_logger = { workspace = true } | ||||||
| russh = { workspace = true } | russh = { workspace = true } | ||||||
| @ -18,11 +18,8 @@ opnsense-config-xml = { path = "../opnsense-config-xml" } | |||||||
| chrono = "0.4.38" | chrono = "0.4.38" | ||||||
| russh-sftp = "2.0.6" | russh-sftp = "2.0.6" | ||||||
| serde_json = "1.0.133" | serde_json = "1.0.133" | ||||||
| tokio-util = { version = "0.7.13", features = ["codec"] } | tokio-util = { version = "0.7.13", features = [ "codec" ] } | ||||||
| tokio-stream = "0.1.17" | tokio-stream = "0.1.17" | ||||||
| 
 | 
 | ||||||
| [dev-dependencies] | [dev-dependencies] | ||||||
| pretty_assertions.workspace = true | pretty_assertions.workspace = true | ||||||
| 
 |  | ||||||
| [lints.rust] |  | ||||||
| unexpected_cfgs = { level = "warn", check-cfg = ['cfg(e2e_test)'] } |  | ||||||
|  | |||||||
| @ -210,7 +210,7 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|     #[tokio::test] |     #[tokio::test] | ||||||
|     async fn test_load_config_from_local_file() { |     async fn test_load_config_from_local_file() { | ||||||
|         for path in [ |         for path in vec![ | ||||||
|             "src/tests/data/config-opnsense-25.1.xml", |             "src/tests/data/config-opnsense-25.1.xml", | ||||||
|             "src/tests/data/config-vm-test.xml", |             "src/tests/data/config-vm-test.xml", | ||||||
|             "src/tests/data/config-structure.xml", |             "src/tests/data/config-structure.xml", | ||||||
| @ -236,9 +236,9 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|             // Since the order of all fields is not always the same in opnsense config files
 |             // Since the order of all fields is not always the same in opnsense config files
 | ||||||
|             // I think it is good enough to have exactly the same amount of the same lines
 |             // I think it is good enough to have exactly the same amount of the same lines
 | ||||||
|             [config_file_str.lines().collect::<Vec<_>>()].sort(); |             let config_file_str_sorted = vec![config_file_str.lines().collect::<Vec<_>>()].sort(); | ||||||
|             [config_file_str.lines().collect::<Vec<_>>()].sort(); |             let serialized_sorted = vec![config_file_str.lines().collect::<Vec<_>>()].sort(); | ||||||
|             assert_eq!((), ()); |             assert_eq!(config_file_str_sorted, serialized_sorted); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -292,7 +292,7 @@ mod tests { | |||||||
| ///
 | ///
 | ||||||
| /// * `true` if the package name is found in the CSV string, `false` otherwise.
 | /// * `true` if the package name is found in the CSV string, `false` otherwise.
 | ||||||
| fn is_package_in_csv(csv_string: &str, package_name: &str) -> bool { | fn is_package_in_csv(csv_string: &str, package_name: &str) -> bool { | ||||||
|     !package_name.is_empty() && csv_string.split(',').any(|pkg| pkg.trim() == package_name) |     package_name.len() > 0 && csv_string.split(',').any(|pkg| pkg.trim() == package_name) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
|  | |||||||
| @ -45,7 +45,7 @@ impl SshConfigManager { | |||||||
|     async fn reload_all_services(&self) -> Result<String, Error> { |     async fn reload_all_services(&self) -> Result<String, Error> { | ||||||
|         info!("Reloading all opnsense services"); |         info!("Reloading all opnsense services"); | ||||||
|         self.opnsense_shell |         self.opnsense_shell | ||||||
|             .exec("configctl service reload all") |             .exec(&format!("configctl service reload all")) | ||||||
|             .await |             .await | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,3 @@ | |||||||
| #[allow(clippy::module_inception)] |  | ||||||
| mod config; | mod config; | ||||||
| mod manager; | mod manager; | ||||||
| mod shell; | mod shell; | ||||||
|  | |||||||
| @ -107,7 +107,7 @@ impl OPNsenseShell for SshOPNSenseShell { | |||||||
|                     match result { |                     match result { | ||||||
|                         Ok(bytes) => { |                         Ok(bytes) => { | ||||||
|                             if !bytes.is_empty() { |                             if !bytes.is_empty() { | ||||||
|                                 AsyncWriteExt::write_all(&mut remote_file, &bytes).await?; |                                 remote_file.write(&bytes).await?; | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         Err(e) => todo!("Error unhandled {e}"), |                         Err(e) => todo!("Error unhandled {e}"), | ||||||
| @ -194,9 +194,9 @@ async fn wait_for_completion(channel: &mut Channel<Msg>) -> Result<String, Error | |||||||
|                     ))); |                     ))); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             russh::ChannelMsg::Success |             russh::ChannelMsg::Success { .. } | ||||||
|             | russh::ChannelMsg::WindowAdjusted { .. } |             | russh::ChannelMsg::WindowAdjusted { .. } | ||||||
|             | russh::ChannelMsg::Eof => {} |             | russh::ChannelMsg::Eof { .. } => {} | ||||||
|             _ => { |             _ => { | ||||||
|                 return Err(Error::Unexpected(format!( |                 return Err(Error::Unexpected(format!( | ||||||
|                     "Russh got unexpected msg {msg:?}" |                     "Russh got unexpected msg {msg:?}" | ||||||
|  | |||||||
| @ -64,7 +64,7 @@ impl<'a> DhcpConfig<'a> { | |||||||
|             .dhcpd |             .dhcpd | ||||||
|             .elements |             .elements | ||||||
|             .iter_mut() |             .iter_mut() | ||||||
|             .find(|(name, _config)| name == "lan") |             .find(|(name, _config)| return name == "lan") | ||||||
|             .expect("Interface lan should have dhcpd activated") |             .expect("Interface lan should have dhcpd activated") | ||||||
|             .1 |             .1 | ||||||
|     } |     } | ||||||
| @ -93,7 +93,11 @@ impl<'a> DhcpConfig<'a> { | |||||||
|                 == ipaddr |                 == ipaddr | ||||||
|                 && m.mac == mac |                 && m.mac == mac | ||||||
|         }) { |         }) { | ||||||
|             info!("Mapping already exists for {} [{}], skipping", ipaddr, mac); |             info!( | ||||||
|  |                 "Mapping already exists for {} [{}], skipping", | ||||||
|  |                 ipaddr.to_string(), | ||||||
|  |                 mac | ||||||
|  |             ); | ||||||
|             return Ok(()); |             return Ok(()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -141,8 +145,9 @@ impl<'a> DhcpConfig<'a> { | |||||||
|             .exec("configctl dhcpd list static") |             .exec("configctl dhcpd list static") | ||||||
|             .await?; |             .await?; | ||||||
| 
 | 
 | ||||||
|         let value: serde_json::Value = serde_json::from_str(&list_static_output) |         let value: serde_json::Value = serde_json::from_str(&list_static_output).expect(&format!( | ||||||
|             .unwrap_or_else(|_| panic!("Got invalid json from configctl {list_static_output}")); |             "Got invalid json from configctl {list_static_output}" | ||||||
|  |         )); | ||||||
|         let static_maps = value["dhcpd"] |         let static_maps = value["dhcpd"] | ||||||
|             .as_array() |             .as_array() | ||||||
|             .ok_or(Error::Command(format!( |             .ok_or(Error::Command(format!( | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user