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

View file

@ -68,9 +68,15 @@ async fn main() -> Result<ExitCode> {
match args {
HazeArgs::Clean => {
let list = Cloud::list(&docker, None, &config).await?;
let (retain, remove) = list
.into_iter()
.partition::<Vec<_>, _>(|cloud| cloud.pinned);
let mut retain = Vec::new();
let mut remove = Vec::new();
for cloud in list {
if cloud.is_pinned(&docker).await.unwrap_or(false) {
retain.push(cloud);
} else {
remove.push(cloud);
}
}
for cloud in remove {
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();
services.push(cloud.db().name());
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() {
Some(version) => format!(", v{version}"),
None => String::new(),