chore(release): stage v1.7.52-alpha
This commit is contained in:
@@ -109,7 +109,7 @@ impl PodmanClient {
|
||||
pub fn lan_address_for(name: &str) -> Option<String> {
|
||||
let url = match name {
|
||||
"bitcoin-knots" | "bitcoin-ui" => "http://localhost:8334",
|
||||
"lnd" | "archy-lnd-ui" => "http://localhost:8081",
|
||||
"lnd" | "archy-lnd-ui" => "http://localhost:18083",
|
||||
"homeassistant" => "http://localhost:8123",
|
||||
"archy-mempool-web" | "mempool" => "http://localhost:4080",
|
||||
"btcpay-server" => "http://localhost:23000",
|
||||
@@ -374,7 +374,10 @@ impl PodmanClient {
|
||||
"env": env_map,
|
||||
"entrypoint": manifest.app.container.entrypoint.clone(),
|
||||
"command": manifest.app.container.custom_args.clone(),
|
||||
"hostadd": ["host.containers.internal:host-gateway"],
|
||||
"hostadd": [
|
||||
"host.containers.internal:host-gateway",
|
||||
"host.archipelago:10.89.0.1",
|
||||
],
|
||||
"devices": manifest.app.devices.iter().map(|d| {
|
||||
serde_json::json!({"path": d})
|
||||
}).collect::<Vec<_>>(),
|
||||
@@ -392,7 +395,10 @@ impl PodmanClient {
|
||||
if let Some(network) = custom_network {
|
||||
body.as_object_mut()
|
||||
.expect("container create body is a JSON object")
|
||||
.insert("networks".to_string(), serde_json::json!({ network: {} }));
|
||||
.insert(
|
||||
"networks".to_string(),
|
||||
serde_json::json!({ network: { "aliases": [name] } }),
|
||||
);
|
||||
}
|
||||
|
||||
let result = self
|
||||
|
||||
@@ -104,7 +104,20 @@ impl ContainerRuntime for PodmanRuntime {
|
||||
}
|
||||
|
||||
async fn list_containers(&self) -> Result<Vec<ContainerStatus>> {
|
||||
self.client.list_containers().await
|
||||
match self.client.list_containers().await {
|
||||
Ok(containers) => Ok(containers),
|
||||
Err(api_err) => {
|
||||
let output = self.podman_cli(&["ps", "-a", "--format", "json"]).await?;
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(
|
||||
api_err.context(format!("podman ps fallback failed: {}", stderr.trim()))
|
||||
);
|
||||
}
|
||||
parse_podman_ps_json(&output.stdout)
|
||||
.with_context(|| format!("podman API list failed: {api_err}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn image_exists(&self, image_ref: &str) -> Result<bool> {
|
||||
@@ -147,6 +160,83 @@ impl ContainerRuntime for PodmanRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_podman_ps_json(stdout: &[u8]) -> Result<Vec<ContainerStatus>> {
|
||||
let text = String::from_utf8_lossy(stdout);
|
||||
if text.trim().is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let containers: Vec<serde_json::Value> = serde_json::from_str(&text)?;
|
||||
Ok(containers
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
let name = c
|
||||
.get("Names")
|
||||
.and_then(|v| v.as_array())
|
||||
.and_then(|a| a.first())
|
||||
.and_then(|v| v.as_str())
|
||||
.or_else(|| c.get("Names").and_then(|v| v.as_str()))
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let status = c.get("Status").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let state = c.get("State").and_then(|v| v.as_str()).unwrap_or("unknown");
|
||||
ContainerStatus {
|
||||
id: c
|
||||
.get("Id")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("")
|
||||
.to_string(),
|
||||
name: name.clone(),
|
||||
state: ContainerState::from(state),
|
||||
health: parse_health_from_status(status),
|
||||
exit_code: c.get("ExitCode").and_then(|v| v.as_i64()).map(|c| c as i32),
|
||||
started_at: c
|
||||
.get("StartedAt")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string()),
|
||||
image: c
|
||||
.get("Image")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("")
|
||||
.to_string(),
|
||||
created: c
|
||||
.get("Created")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("")
|
||||
.to_string(),
|
||||
ports: parse_podman_ps_ports(c.get("Ports")),
|
||||
lan_address: PodmanClient::lan_address_for(&name),
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn parse_podman_ps_ports(ports: Option<&serde_json::Value>) -> Vec<String> {
|
||||
ports
|
||||
.and_then(|v| v.as_array())
|
||||
.map(|ports| {
|
||||
ports
|
||||
.iter()
|
||||
.filter_map(|port| {
|
||||
let host = port.get("host_port").and_then(|v| v.as_u64())?;
|
||||
let container = port.get("container_port").and_then(|v| v.as_u64())?;
|
||||
let proto = port
|
||||
.get("protocol")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("tcp");
|
||||
Some(format!("0.0.0.0:{host}->{container}/{proto}"))
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn parse_health_from_status(status: &str) -> Option<String> {
|
||||
let start = status.rfind('(')?;
|
||||
let end = status.rfind(')')?;
|
||||
(start < end).then(|| status[start + 1..end].to_string())
|
||||
}
|
||||
|
||||
/// Build the argv for `podman build` from a BuildConfig.
|
||||
///
|
||||
/// Extracted so it can be unit-tested without actually invoking podman.
|
||||
@@ -646,4 +736,36 @@ mod tests {
|
||||
let args = build_args_for_podman(&c);
|
||||
assert_eq!(args.last().unwrap(), "/final/context");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_podman_ps_json_handles_cli_output() {
|
||||
let stdout = br#"[
|
||||
{
|
||||
"Id": "abc123",
|
||||
"Names": ["mempool"],
|
||||
"Image": "docker.io/mempool/frontend:latest",
|
||||
"State": "running",
|
||||
"Status": "Up 2 minutes (healthy)",
|
||||
"Created": "2026-05-03T00:00:00Z",
|
||||
"StartedAt": "2026-05-03T00:01:00Z",
|
||||
"ExitCode": 0,
|
||||
"Ports": [
|
||||
{
|
||||
"host_port": 4080,
|
||||
"container_port": 8080,
|
||||
"protocol": "tcp"
|
||||
}
|
||||
]
|
||||
}
|
||||
]"#;
|
||||
|
||||
let containers = parse_podman_ps_json(stdout).unwrap();
|
||||
assert_eq!(containers.len(), 1);
|
||||
assert_eq!(containers[0].id, "abc123");
|
||||
assert_eq!(containers[0].name, "mempool");
|
||||
assert_eq!(containers[0].state, ContainerState::Running);
|
||||
assert_eq!(containers[0].health.as_deref(), Some("healthy"));
|
||||
assert_eq!(containers[0].exit_code, Some(0));
|
||||
assert_eq!(containers[0].ports, vec!["0.0.0.0:4080->8080/tcp"]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user