refactor(container): drop unused dependency_resolver module

DependencyResolver had zero call sites in prod or tests outside the
module itself. The actual install-time dependency check lives in
install.rs::detect_running_deps + check_install_deps; this DAG-walk
solver was never wired up. -268 LoC.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-05-01 15:22:07 -04:00
parent 8321d093e8
commit a1f37b20ed
2 changed files with 0 additions and 270 deletions

View File

@@ -1,268 +0,0 @@
use crate::manifest::{AppManifest, Dependency};
use indexmap::IndexMap;
use std::collections::{HashMap, HashSet};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum DependencyError {
#[error("Circular dependency detected: {0}")]
CircularDependency(String),
#[error("Missing dependency: {0}")]
MissingDependency(String),
#[error("Version conflict: {0}")]
VersionConflict(String),
}
pub struct DependencyResolver {
manifests: IndexMap<String, AppManifest>,
}
impl DependencyResolver {
pub fn new() -> Self {
Self {
manifests: IndexMap::new(),
}
}
pub fn add_manifest(&mut self, manifest: AppManifest) {
self.manifests.insert(manifest.app.id.clone(), manifest);
}
pub fn resolve_dependencies(&self, app_id: &str) -> Result<Vec<String>, DependencyError> {
let mut visited = HashSet::new();
let mut visiting = HashSet::new();
let mut result = Vec::new();
self.resolve_recursive(app_id, &mut visited, &mut visiting, &mut result)?;
// Result is already in installation order (dependencies first)
Ok(result)
}
fn resolve_recursive(
&self,
app_id: &str,
visited: &mut HashSet<String>,
visiting: &mut HashSet<String>,
result: &mut Vec<String>,
) -> Result<(), DependencyError> {
if visited.contains(app_id) {
return Ok(());
}
if visiting.contains(app_id) {
return Err(DependencyError::CircularDependency(format!(
"Circular dependency detected involving: {}",
app_id
)));
}
visiting.insert(app_id.to_string());
let manifest = self.manifests.get(app_id).ok_or_else(|| {
DependencyError::MissingDependency(format!("App not found: {}", app_id))
})?;
// Resolve all dependencies first
for dep in &manifest.app.dependencies {
match dep {
Dependency::App {
app_id: dep_id,
version: _,
} => {
self.resolve_recursive(dep_id, visited, visiting, result)?;
}
Dependency::Storage { storage: _ } => {
// Storage dependencies are checked but don't require other apps
}
Dependency::Simple(dep_id) => {
self.resolve_recursive(dep_id, visited, visiting, result)?;
}
}
}
visiting.remove(app_id);
visited.insert(app_id.to_string());
if !result.contains(&app_id.to_string()) {
result.push(app_id.to_string());
}
Ok(())
}
pub fn check_conflicts(&self, app_id: &str) -> Result<(), DependencyError> {
let manifest = self.manifests.get(app_id).ok_or_else(|| {
DependencyError::MissingDependency(format!("App not found: {}", app_id))
})?;
// Check for port conflicts
let mut port_usage: HashMap<u16, String> = HashMap::new();
for (id, m) in &self.manifests {
if id == app_id {
continue;
}
for port in &m.app.ports {
if let Some(existing) = port_usage.get(&port.host) {
return Err(DependencyError::VersionConflict(format!(
"Port {} already used by {}",
port.host, existing
)));
}
port_usage.insert(port.host, id.clone());
}
}
// Check for new app's ports
for port in &manifest.app.ports {
if let Some(existing) = port_usage.get(&port.host) {
return Err(DependencyError::VersionConflict(format!(
"Port {} already used by {}",
port.host, existing
)));
}
}
Ok(())
}
pub fn calculate_resources(&self, app_ids: &[String]) -> ResourceRequirements {
let mut total = ResourceRequirements {
cpu: 0,
memory_mb: 0,
disk_gb: 0,
};
for app_id in app_ids {
if let Some(manifest) = self.manifests.get(app_id) {
if let Some(cpu) = manifest.app.resources.cpu_limit {
total.cpu += cpu;
}
if let Some(memory) = &manifest.app.resources.memory_limit {
// Parse memory string (e.g., "1Gi", "512Mi")
if let Ok(mb) = parse_memory(memory) {
total.memory_mb += mb;
}
}
if let Some(disk) = &manifest.app.resources.disk_limit {
// Parse disk string (e.g., "10Gi", "500Mi")
if let Ok(gb) = parse_disk(disk) {
total.disk_gb += gb;
}
}
}
}
total
}
}
fn parse_memory(s: &str) -> Result<u32, ()> {
let s = s.trim().to_lowercase();
if s.ends_with("gi") {
let num: f64 = s.trim_end_matches("gi").parse().map_err(|_| ())?;
Ok((num * 1024.0) as u32)
} else if s.ends_with("mi") {
let num: f64 = s.trim_end_matches("mi").parse().map_err(|_| ())?;
Ok(num as u32)
} else {
Err(())
}
}
fn parse_disk(s: &str) -> Result<u32, ()> {
let s = s.trim().to_lowercase();
if s.ends_with("gi") {
let num: f64 = s.trim_end_matches("gi").parse().map_err(|_| ())?;
Ok(num as u32)
} else if s.ends_with("ti") {
let num: f64 = s.trim_end_matches("ti").parse().map_err(|_| ())?;
Ok((num * 1024.0) as u32)
} else {
Err(())
}
}
#[derive(Debug, Clone)]
pub struct ResourceRequirements {
pub cpu: u32,
pub memory_mb: u32,
pub disk_gb: u32,
}
impl Default for DependencyResolver {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::manifest::{AppDefinition, AppManifest, ContainerConfig};
fn create_test_manifest(id: &str, deps: Vec<Dependency>) -> AppManifest {
AppManifest {
app: AppDefinition {
id: id.to_string(),
name: format!("Test {}", id),
version: "1.0.0".to_string(),
description: None,
container: ContainerConfig {
image: Some(format!("test/{}:latest", id)),
image_signature: None,
pull_policy: "if-not-present".to_string(),
build: None,
network: None,
custom_args: vec![],
entrypoint: None,
derived_env: vec![],
secret_env: vec![],
data_uid: None,
},
dependencies: deps,
resources: Default::default(),
security: Default::default(),
ports: vec![],
volumes: vec![],
environment: vec![],
health_check: None,
devices: vec![],
extensions: Default::default(),
},
}
}
#[test]
fn test_simple_dependency() {
let mut resolver = DependencyResolver::new();
resolver.add_manifest(create_test_manifest("app1", vec![]));
resolver.add_manifest(create_test_manifest(
"app2",
vec![Dependency::Simple("app1".to_string())],
));
let deps = resolver.resolve_dependencies("app2").unwrap();
assert_eq!(deps, vec!["app1", "app2"]);
}
#[test]
fn test_circular_dependency() {
let mut resolver = DependencyResolver::new();
resolver.add_manifest(create_test_manifest(
"app1",
vec![Dependency::Simple("app2".to_string())],
));
resolver.add_manifest(create_test_manifest(
"app2",
vec![Dependency::Simple("app1".to_string())],
));
let result = resolver.resolve_dependencies("app1");
assert!(result.is_err());
}
}

View File

@@ -1,5 +1,4 @@
pub mod bitcoin_simulator;
pub mod dependency_resolver;
pub mod health_monitor;
pub mod manifest;
pub mod podman_client;
@@ -7,7 +6,6 @@ pub mod port_manager;
pub mod runtime;
pub use bitcoin_simulator::{BitcoinSimulationMode, BitcoinSimulator};
pub use dependency_resolver::DependencyResolver;
pub use health_monitor::HealthMonitor;
pub use manifest::{
AppManifest, BuildConfig, ContainerConfig, Dependency, DerivedEnv, HealthCheck, HostFacts,