fix(orchestrator): generalize launch-port fallback + archival-bitcoin dependency gating
Master-plan backlog §10b/§10c: replace two per-app-hardcoded lookups with generic, manifest-driven behavior so future apps are covered automatically instead of needing a code edit. - extract_lan_address (docker_packages.rs) now skips container-side ports that are known non-HTTP (SSH, FTP, common DB ports) instead of blindly taking podman's first-listed port. Fixes the whole class of bug the gitea SSH-before-web static override was a one-off patch for. - requires_unpruned_bitcoin (dependencies.rs) now checks the app's own manifest for a `bitcoin:archival` dependency declaration first, falling back to the old hardcoded id list. electrumx and mempool manifests now declare it explicitly as the proof case. 869/869 Rust tests green, catalog drift clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
46dae75a0f
commit
306b6356ee
@@ -1,6 +1,8 @@
|
||||
use super::config::get_containers_for_app;
|
||||
use super::runtime::manifest_apps_dirs;
|
||||
use crate::data_model::{PackageDataEntry, PackageState};
|
||||
use anyhow::{Context, Result};
|
||||
use archipelago_container::{AppManifest, Dependency};
|
||||
use std::collections::HashMap;
|
||||
use tracing::info;
|
||||
|
||||
@@ -11,7 +13,38 @@ const BITCOIN_NAMES: &[&str] = &["bitcoin-knots", "bitcoin-core", "bitcoin"];
|
||||
const ELECTRUM_NAMES: &[&str] = &["electrumx", "mempool-electrs", "electrs"];
|
||||
const ARCHIVAL_BITCOIN_DISK_GB: u64 = 1000;
|
||||
|
||||
/// The manifest string dependency that declares "needs an archival
|
||||
/// (unpruned + txindex) Bitcoin node" — see `manifest_declares_archival_bitcoin`.
|
||||
const ARCHIVAL_BITCOIN_DEPENDENCY: &str = "bitcoin:archival";
|
||||
|
||||
/// Whether `package_id`'s own on-disk manifest declares
|
||||
/// `dependencies: [bitcoin:archival]`. Manifest-driven alternative to the
|
||||
/// hardcoded id list below — a new app just declares the dependency instead
|
||||
/// of needing a code change here.
|
||||
fn manifest_declares_archival_bitcoin(package_id: &str) -> bool {
|
||||
for apps_dir in manifest_apps_dirs() {
|
||||
let path = apps_dir.join(package_id).join("manifest.yml");
|
||||
let Ok(contents) = std::fs::read_to_string(&path) else {
|
||||
continue;
|
||||
};
|
||||
let Ok(manifest) = AppManifest::parse(&contents) else {
|
||||
continue;
|
||||
};
|
||||
return dependency_list_declares_archival_bitcoin(&manifest.app.dependencies);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn dependency_list_declares_archival_bitcoin(deps: &[Dependency]) -> bool {
|
||||
deps.iter()
|
||||
.any(|dep| matches!(dep, Dependency::Simple(s) if s == ARCHIVAL_BITCOIN_DEPENDENCY))
|
||||
}
|
||||
|
||||
fn requires_unpruned_bitcoin(package_id: &str) -> bool {
|
||||
if manifest_declares_archival_bitcoin(package_id) {
|
||||
return true;
|
||||
}
|
||||
// Fallback for apps not yet migrated to the manifest declaration above.
|
||||
matches!(
|
||||
package_id,
|
||||
"electrumx" | "mempool-electrs" | "electrs" | "mempool" | "mempool-web"
|
||||
@@ -473,7 +506,11 @@ pub(super) fn configure_fedimint_lnd(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{order_present_containers, requires_unpruned_bitcoin, startup_order};
|
||||
use super::{
|
||||
dependency_list_declares_archival_bitcoin, manifest_declares_archival_bitcoin,
|
||||
order_present_containers, requires_unpruned_bitcoin, startup_order,
|
||||
};
|
||||
use archipelago_container::Dependency;
|
||||
|
||||
#[test]
|
||||
fn order_present_containers_never_injects_phantom_stack_members() {
|
||||
@@ -547,4 +584,44 @@ mod tests {
|
||||
assert!(!requires_unpruned_bitcoin(package_id), "{package_id}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dependency_matcher_finds_the_archival_marker_among_other_deps() {
|
||||
let deps = vec![
|
||||
Dependency::App {
|
||||
app_id: "bitcoin-knots".to_string(),
|
||||
version: Some(">=26.0".to_string()),
|
||||
},
|
||||
Dependency::Storage {
|
||||
storage: "50Gi".to_string(),
|
||||
},
|
||||
Dependency::Simple("bitcoin:archival".to_string()),
|
||||
];
|
||||
assert!(dependency_list_declares_archival_bitcoin(&deps));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dependency_matcher_false_when_marker_absent() {
|
||||
let deps = vec![Dependency::App {
|
||||
app_id: "bitcoin-knots".to_string(),
|
||||
version: Some(">=26.0".to_string()),
|
||||
}];
|
||||
assert!(!dependency_list_declares_archival_bitcoin(&deps));
|
||||
assert!(!dependency_list_declares_archival_bitcoin(&[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_declared_archival_bitcoin_covers_a_new_app_without_a_code_change() {
|
||||
// electrumx and mempool declare `dependencies: [..., bitcoin:archival]`
|
||||
// on disk (apps/electrumx/manifest.yml, apps/mempool/manifest.yml) —
|
||||
// this is the manifest-driven path working end-to-end, not the
|
||||
// hardcoded id list. A future app only needs this manifest line, no
|
||||
// edit to `requires_unpruned_bitcoin`.
|
||||
assert!(manifest_declares_archival_bitcoin("electrumx"));
|
||||
assert!(manifest_declares_archival_bitcoin("mempool"));
|
||||
// An app whose manifest exists but never declares the marker.
|
||||
assert!(!manifest_declares_archival_bitcoin("bitcoin-knots"));
|
||||
// An id with no manifest on disk at all.
|
||||
assert!(!manifest_declares_archival_bitcoin("does-not-exist"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1603,7 +1603,7 @@ fn manifest_host_ports(container_name: &str) -> Vec<u16> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn manifest_apps_dirs() -> Vec<std::path::PathBuf> {
|
||||
pub(super) fn manifest_apps_dirs() -> Vec<std::path::PathBuf> {
|
||||
let mut dirs = Vec::new();
|
||||
if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") {
|
||||
dirs.push(Path::new(&manifest_dir).join("../../apps"));
|
||||
|
||||
Reference in New Issue
Block a user