use crate::core::detector_registry::DetectorRegistry;
use crate::health::{HealthCheckerRegistry, ScriptHealthChecker};
use crate::logs::{log_registry::LogStreamerRegistry, script::ScriptLogStreamer};
use crate::models::HealthCheckType;
use crate::models::Service;
use crate::packs::loader::ServicePackLoader;
use crate::packs::schema::ServicePack;
use anyhow::Result;
use std::path::Path;
use tracing::{debug, info};
pub struct PackIntegration;
impl PackIntegration {
pub fn integrate_packs(
project_path: &Path,
_detector_registry: &mut DetectorRegistry,
health_registry: &mut HealthCheckerRegistry,
log_registry: &mut LogStreamerRegistry,
) -> Result<Vec<ServicePack>> {
let packs = ServicePackLoader::load_all(project_path)?;
if packs.is_empty() {
return Ok(packs);
}
info!("Integrating {} service pack(s)", packs.len());
for pack in &packs {
for signature in &pack.signatures {
if let Some(ref health_check) = signature.health_check {
if let HealthCheckType::Custom { ref script } = health_check.check_type {
let checker_id = format!("pack:{}:{}", pack.name, signature.name);
let checker_id_clone = checker_id.clone();
let checker = ScriptHealthChecker::new(
script.clone(),
health_check.timeout,
);
health_registry.register(checker_id, Box::new(checker));
debug!("Registered script health checker: {}", checker_id_clone);
}
}
}
for signature in &pack.signatures {
for log_path in &signature.log_paths {
if log_path.contains(' ') && !log_path.starts_with('/') && !log_path.starts_with('.') {
let streamer_id = format!("pack:{}:{}:logs", pack.name, signature.name);
let streamer_id_clone = streamer_id.clone();
let streamer = ScriptLogStreamer::new(
log_path.clone(),
Some(format!("{}:{}", pack.name, signature.name)),
);
log_registry.register(streamer_id, Box::new(streamer));
debug!("Registered script log streamer: {}", streamer_id_clone);
}
}
}
}
Ok(packs)
}
pub fn apply_pack_defaults(service: &mut Service, packs: &[ServicePack]) {
for pack in packs {
for (pattern, health_check) in &pack.default_health_checks {
if Self::matches_pattern(&service.name, pattern) {
if service.health_check.check_type == HealthCheckType::Port {
service.health_check = health_check.clone();
debug!("Applied health check from pack '{}' to service '{}'", pack.name, service.name);
}
}
}
for (pattern, log_paths) in &pack.default_log_paths {
if Self::matches_pattern(&service.name, pattern) {
if service.log_file_path.is_none() && !log_paths.is_empty() {
service.log_file_path = Some(log_paths[0].clone());
debug!("Applied log path from pack '{}' to service '{}'", pack.name, service.name);
}
}
}
for (pattern, unit) in &pack.default_systemd_units {
if Self::matches_pattern(&service.name, pattern) {
if service.systemd_unit.is_none() {
service.systemd_unit = Some(unit.clone());
debug!("Applied systemd unit from pack '{}' to service '{}'", pack.name, service.name);
}
}
}
for signature in &pack.signatures {
if Self::signature_matches(service, signature) {
if service.role == crate::models::ServiceRole::Unknown {
service.role = signature.role;
}
if let Some(ref health_check) = signature.health_check {
if service.health_check.check_type == HealthCheckType::Port {
service.health_check = health_check.clone();
}
}
if service.log_file_path.is_none() && !signature.log_paths.is_empty() {
service.log_file_path = Some(signature.log_paths[0].clone());
}
if service.systemd_unit.is_none() {
if let Some(ref unit) = signature.systemd_unit {
service.systemd_unit = Some(unit.clone());
}
}
debug!("Applied signature from pack '{}' to service '{}'", pack.name, service.name);
break;
}
}
}
}
fn matches_pattern(name: &str, pattern: &str) -> bool {
name.to_lowercase().contains(&pattern.to_lowercase())
}
fn signature_matches(service: &Service, signature: &crate::packs::schema::ServicePackSignature) -> bool {
if let Some(port) = signature.port {
if service.port != port {
return false;
}
}
if let Some(ref pattern) = signature.process_pattern {
if let Some(ref process_name) = service.process_name {
if !process_name.to_lowercase().contains(&pattern.to_lowercase()) {
return false;
}
} else {
return false;
}
}
if let Some(ref unit_pattern) = signature.systemd_unit {
if let Some(ref unit) = service.systemd_unit {
if !unit.contains(unit_pattern) {
return false;
}
} else {
return false;
}
}
true
}
}