1
0
Fork 0
mirror of https://codeberg.org/icewind/haze.git synced 2026-10-01 16:54:08 +02:00

feat(pin): Store pinned state in a file within the container

Signed-off-by: Louis Chmn <louis@chmn.me>
This commit is contained in:
Louis Chmn 2026-08-08 21:00:20 +02:00
commit 8841570e0a
2 changed files with 39 additions and 43 deletions

View file

@ -2,12 +2,12 @@ use crate::config::{HazeConfig, HazeVolumeConfig};
use crate::database::Database; use crate::database::Database;
use crate::exec::{exec, exec_io, exec_tty, ExitCode}; use crate::exec::{exec, exec_io, exec_tty, ExitCode};
use crate::mapping::{for_config, Mapping}; use crate::mapping::{for_config, Mapping};
use crate::php::{PhpVersion, PHP_MEMORY_LIMIT}; use crate::php::PhpVersion;
use crate::service::Service; use crate::service::Service;
use crate::service::ServiceTrait; use crate::service::ServiceTrait;
use crate::sources::download_nc; use crate::sources::download_nc;
use bollard::config::NetworkCreateRequest; use bollard::config::NetworkCreateRequest;
use bollard::models::{ContainerState, ContainerUpdateBody}; use bollard::models::ContainerState;
use bollard::query_parameters::{ListContainersOptions, RemoveContainerOptions}; use bollard::query_parameters::{ListContainersOptions, RemoveContainerOptions};
use bollard::Docker; use bollard::Docker;
use camino::{Utf8Path, Utf8PathBuf}; use camino::{Utf8Path, Utf8PathBuf};
@ -51,6 +51,8 @@ fn get_max_php_version(base_path: &Utf8Path) -> Option<PhpVersion> {
PhpVersion::from_number(major, minor) PhpVersion::from_number(major, minor)
} }
const PINNED_FILE: &str = "/var/www/html/data/.haze-pinned";
#[derive(Clone, Debug, Eq, PartialEq, Default)] #[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct CloudOptions { pub struct CloudOptions {
pub name: Option<String>, pub name: Option<String>,
@ -226,7 +228,6 @@ pub struct Cloud {
pub ip: Option<IpAddr>, pub ip: Option<IpAddr>,
pub workdir: Utf8PathBuf, pub workdir: Utf8PathBuf,
pub options: CloudOptions, pub options: CloudOptions,
pub pinned: bool,
pub address: String, pub address: String,
pub preset_config: HashMap<String, Value>, pub preset_config: HashMap<String, Value>,
} }
@ -555,7 +556,6 @@ impl Cloud {
ip: Some(ip), ip: Some(ip),
workdir, workdir,
options, options,
pinned: false,
address, address,
preset_config, preset_config,
}) })
@ -671,7 +671,7 @@ impl Cloud {
})) }))
.await .await
.into_diagnostic()?; .into_diagnostic()?;
let mut containers_by_id: HashMap<String, (Option<_>, Option<_>, Vec<_>)> = HashMap::new(); let mut containers_by_id: HashMap<String, (Option<_>, Vec<_>)> = HashMap::new();
for container in containers { for container in containers {
let labels = container.labels.clone().unwrap_or_default(); let labels = container.labels.clone().unwrap_or_default();
if let Some(cloud_id) = labels.get("haze-cloud-id") { if let Some(cloud_id) = labels.get("haze-cloud-id") {
@ -681,11 +681,9 @@ impl Cloud {
} { } {
let entry = containers_by_id.entry(cloud_id.to_string()).or_default(); let entry = containers_by_id.entry(cloud_id.to_string()).or_default();
if labels.get("haze-type").map(String::as_str) == Some("cloud") { if labels.get("haze-type").map(String::as_str) == Some("cloud") {
let info = docker.inspect_container(cloud_id, None).await.ok();
entry.0 = Some(container); entry.0 = Some(container);
entry.1 = info;
} else { } else {
entry.2.push(container) entry.1.push(container)
} }
} }
} }
@ -693,7 +691,7 @@ impl Cloud {
let mut sortable_containers: Vec<_> = containers_by_id let mut sortable_containers: Vec<_> = containers_by_id
.into_iter() .into_iter()
.filter_map(|(id, (cloud, info, services))| { .filter_map(|(id, (cloud, services))| {
let cloud = cloud?; let cloud = cloud?;
let network = id.clone(); let network = id.clone();
let networks = cloud.network_settings?.networks?; let networks = cloud.network_settings?.networks?;
@ -719,13 +717,6 @@ impl Cloud {
.filter_map(|service| service.names.as_ref()?.first().cloned()) .filter_map(|service| service.names.as_ref()?.first().cloned())
.collect(); .collect();
let pinned = (info
.and_then(|info| info.host_config)
.and_then(|host| host.memory)
.unwrap()
% 2)
== 1;
let ip = network_info.ip_address.as_ref()?.parse().ok(); let ip = network_info.ip_address.as_ref()?.parse().ok();
let address = if let Some(ip) = ip { let address = if let Some(ip) = ip {
config.proxy.addr(&id, ip) config.proxy.addr(&id, ip)
@ -750,7 +741,6 @@ impl Cloud {
app_packages: vec![], app_packages: vec![],
version, version,
}, },
pinned,
address, address,
preset_config: HashMap::default(), preset_config: HashMap::default(),
}, },
@ -814,32 +804,28 @@ impl Cloud {
Ok(()) Ok(())
} }
pub async fn pin(&self, docker: &Docker) -> Result<()> { pub async fn is_pinned(&self, docker: &Docker) -> Result<bool> {
// abuse memory limits as editable label let mut content = Vec::new();
docker self.exec_with_output(
.update_container( docker,
&self.id, vec!["cat", PINNED_FILE],
ContainerUpdateBody { Some(&mut content),
memory: Some(PHP_MEMORY_LIMIT + 1), Vec::<String>::new(),
..ContainerUpdateBody::default()
},
) )
.await .await?
.to_result()?;
String::from_utf8(content)
.into_diagnostic() .into_diagnostic()
.map(|content| content.trim() == "1")
}
pub async fn pin(&self, docker: &Docker) -> Result<()> {
self.write_file(docker, PINNED_FILE, "1").await
} }
pub async fn unpin(&self, docker: &Docker) -> Result<()> { pub async fn unpin(&self, docker: &Docker) -> Result<()> {
// abuse memory limits as editable label self.write_file(docker, PINNED_FILE, "0").await
docker
.update_container(
&self.id,
ContainerUpdateBody {
memory: Some(PHP_MEMORY_LIMIT),
..ContainerUpdateBody::default()
},
)
.await
.into_diagnostic()
} }
pub fn services(&self) -> impl Iterator<Item = &Service> { pub fn services(&self) -> impl Iterator<Item = &Service> {

View file

@ -68,9 +68,15 @@ async fn main() -> Result<ExitCode> {
match args { match args {
HazeArgs::Clean => { HazeArgs::Clean => {
let list = Cloud::list(&docker, None, &config).await?; let list = Cloud::list(&docker, None, &config).await?;
let (retain, remove) = list let mut retain = Vec::new();
.into_iter() let mut remove = Vec::new();
.partition::<Vec<_>, _>(|cloud| cloud.pinned); for cloud in list {
if cloud.is_pinned(&docker).await.unwrap_or(false) {
retain.push(cloud);
} else {
remove.push(cloud);
}
}
for cloud in remove { for cloud in remove {
if let Err(e) = cloud.destroy(&docker).await { if let Err(e) = cloud.destroy(&docker).await {
@ -102,7 +108,11 @@ async fn main() -> Result<ExitCode> {
let mut services: Vec<_> = cloud.services().map(Service::name).collect(); let mut services: Vec<_> = cloud.services().map(Service::name).collect();
services.push(cloud.db().name()); services.push(cloud.db().name());
let services = services.join(", "); let services = services.join(", ");
let pin = if cloud.pinned { "*" } else { "" }; let pin = if cloud.is_pinned(&docker).await.unwrap_or(false) {
"*"
} else {
""
};
let version = match cloud.options.version.as_ref() { let version = match cloud.options.version.as_ref() {
Some(version) => format!(", v{version}"), Some(version) => format!(", v{version}"),
None => String::new(), None => String::new(),