From fdc821cb93f3f06b4fd53cc34474310873ab005d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 17:35:47 +0100 Subject: [PATCH] allow bare-service proxy hosts --- src/cloud.rs | 8 +++++- src/proxy.rs | 74 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/src/cloud.rs b/src/cloud.rs index 7232e33..a1c39c9 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -218,7 +218,7 @@ fn test_option_parse() { ); } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Cloud { pub id: String, pub network: String, @@ -851,3 +851,9 @@ impl Cloud { None } } + +impl PartialEq for Cloud { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} diff --git a/src/proxy.rs b/src/proxy.rs index ee867c9..c99c216 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,4 +1,4 @@ -use crate::service::ServiceTrait; +use crate::service::{ServiceTrait, ServiceType}; use crate::Result; use crate::{Cloud, HazeConfig}; use axum::http::header::HOST; @@ -18,6 +18,7 @@ use std::fs::{create_dir_all, set_permissions}; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; +use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::Duration; use tokio::net::UnixListener; @@ -28,7 +29,7 @@ use tracing::{debug, error, info}; struct ActiveInstances { known: Mutex>, - last: Mutex>, + last: Mutex>, docker: Docker, config: HazeConfig, } @@ -48,15 +49,9 @@ impl ActiveInstances { return Some(ip); } - // service proxy - let addr = if name.matches('-').count() == 2 { - let (name, service_name) = name.rsplit_once('-').unwrap(); - let cloud = Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config) - .await - .ok()?; - let service = cloud - .services() - .find(|service| service.name() == service_name)?; + let addr = if ServiceType::from_str(name).is_ok() { + let cloud = self.last()?; + let service = cloud.services().find(|service| service.name() == name)?; let ip = service .get_ips(&self.docker, &cloud.id) .await @@ -64,13 +59,33 @@ impl ActiveInstances { .next()?; SocketAddr::new(ip, service.proxy_port()) } else { - SocketAddr::new( - Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config) - .await - .ok()? - .ip?, - 80, - ) + match name.matches('-').count() { + // instance + 1 => SocketAddr::new( + Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config) + .await + .ok()? + .ip?, + 80, + ), + // service with instance + 2 => { + let (name, service_name) = name.rsplit_once('-').unwrap(); + let cloud = Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config) + .await + .ok()?; + let service = cloud + .services() + .find(|service| service.name() == service_name)?; + let ip = service + .get_ips(&self.docker, &cloud.id) + .await + .ok()? + .next()?; + SocketAddr::new(ip, service.proxy_port()) + } + _ => return None, + } }; println!("{name} => {addr}"); @@ -79,18 +94,31 @@ impl ActiveInstances { Some(addr) } - pub fn last(&self) -> Option { - *self.last.lock().unwrap() + pub fn last_addr(&self) -> Option { + self.last + .lock() + .unwrap() + .as_ref() + .and_then(|cloud| Some(SocketAddr::new(cloud.ip?, 80))) + } + + pub fn last(&self) -> Option { + self.last.lock().unwrap().clone() } async fn update_last(&self) { let last = Cloud::get_by_filter(&self.docker, None, &self.config) .await - .ok() - .and_then(|cloud| Some(SocketAddr::new(cloud.ip?, 80))); + .ok(); let mut old = self.last.lock().unwrap(); if old.as_ref() != last.as_ref() { info!(instance = ?last, "Found new instance"); + + // remove cached base-service mappings + self.known + .lock() + .unwrap() + .retain(|key, _| ServiceType::from_str(key).is_err()); *old = last; } } @@ -181,7 +209,7 @@ async fn get_remote( }; let ip = if host == base_address { instances - .last() + .last_addr() .ok_or_else(|| String::from("No running instance known")) } else { let requested_instance = host.split('.').next().unwrap();