1
0
Fork 0
mirror of https://codeberg.org/icewind/haze.git synced 2026-06-03 09:04:12 +02:00

allow pinning instances

This commit is contained in:
Robin Appelman 2022-07-21 14:46:39 +02:00
commit 81695aee4b
6 changed files with 93 additions and 14 deletions

View file

@ -132,6 +132,20 @@ haze [match] logs
haze [match] stop haze [match] stop
``` ```
#### Pin an instance
```bash
haze [match] pin
```
Pinned instances will not be removed by `haze clean`.
#### Unpin an instance
```bash
haze [match] unpin
```
## Configuration ## Configuration
Configuration is loaded from `~/.config/haze/haze.toml` and has the following options Configuration is loaded from `~/.config/haze/haze.toml` and has the following options

View file

@ -54,6 +54,12 @@ pub enum HazeArgs {
options: CloudOptions, options: CloudOptions,
command: Vec<String>, command: Vec<String>,
}, },
Pin {
filter: Option<String>,
},
Unpin {
filter: Option<String>,
},
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -202,6 +208,8 @@ impl HazeArgs {
let command = args.map(S::into).collect(); let command = args.map(S::into).collect();
Ok(HazeArgs::Shell { options, command }) Ok(HazeArgs::Shell { options, command })
} }
HazeCommand::Pin => Ok(HazeArgs::Pin { filter }),
HazeCommand::Unpin => Ok(HazeArgs::Unpin { filter }),
} }
} }
} }
@ -221,6 +229,8 @@ pub enum HazeCommand {
Fmt, Fmt,
Integration, Integration,
Shell, Shell,
Pin,
Unpin,
} }
impl FromStr for HazeCommand { impl FromStr for HazeCommand {
@ -242,6 +252,8 @@ impl FromStr for HazeCommand {
"format" => Ok(HazeCommand::Fmt), "format" => Ok(HazeCommand::Fmt),
"integration" => Ok(HazeCommand::Integration), "integration" => Ok(HazeCommand::Integration),
"shell" => Ok(HazeCommand::Shell), "shell" => Ok(HazeCommand::Shell),
"pin" => Ok(HazeCommand::Pin),
"unpin" => Ok(HazeCommand::Unpin),
_ => Err(Report::msg(format!("Unknown command: {}", s))), _ => Err(Report::msg(format!("Unknown command: {}", s))),
} }
} }
@ -263,6 +275,8 @@ impl HazeCommand {
HazeCommand::Fmt => false, HazeCommand::Fmt => false,
HazeCommand::Integration => false, HazeCommand::Integration => false,
HazeCommand::Shell => false, HazeCommand::Shell => false,
HazeCommand::Pin => true,
HazeCommand::Unpin => true,
} }
} }
} }

View file

@ -2,10 +2,10 @@ use crate::config::{HazeConfig, HazeVolumeConfig};
use crate::database::Database; use crate::database::Database;
use crate::exec::{exec, exec_tty, ExitCode}; use crate::exec::{exec, exec_tty, ExitCode};
use crate::mapping::{default_mappings, Mapping}; use crate::mapping::{default_mappings, Mapping};
use crate::php::PhpVersion; use crate::php::{PhpVersion, PHP_MEMORY_LIMIT};
use crate::service::Service; use crate::service::Service;
use crate::service::ServiceTrait; use crate::service::ServiceTrait;
use bollard::container::{ListContainersOptions, RemoveContainerOptions}; use bollard::container::{ListContainersOptions, RemoveContainerOptions, UpdateContainerOptions};
use bollard::models::ContainerState; use bollard::models::ContainerState;
use bollard::network::CreateNetworkOptions; use bollard::network::CreateNetworkOptions;
use bollard::Docker; use bollard::Docker;
@ -157,6 +157,7 @@ pub struct Cloud {
pub ip: Option<IpAddr>, pub ip: Option<IpAddr>,
pub workdir: Utf8PathBuf, pub workdir: Utf8PathBuf,
pub services: Vec<Service>, pub services: Vec<Service>,
pub pinned: bool,
} }
impl Cloud { impl Cloud {
@ -167,6 +168,7 @@ impl Cloud {
) -> Result<Self> { ) -> Result<Self> {
let id = options let id = options
.name .name
.map(|name| format!("haze-{}", name))
.unwrap_or_else(|| format!("haze-{}", petname(2, "-"))); .unwrap_or_else(|| format!("haze-{}", petname(2, "-")));
let workdir = config.work_dir.join(&id); let workdir = config.work_dir.join(&id);
@ -380,6 +382,7 @@ impl Cloud {
ip: Some(ip), ip: Some(ip),
workdir, workdir,
services: options.services, services: options.services,
pinned: false,
}) })
} }
@ -459,7 +462,7 @@ impl Cloud {
})) }))
.await .await
.into_diagnostic()?; .into_diagnostic()?;
let mut containers_by_id: HashMap<String, (Option<_>, Vec<_>)> = HashMap::new(); let mut containers_by_id: HashMap<String, (Option<_>, 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") {
@ -469,9 +472,11 @@ impl Cloud {
} { } {
let mut entry = containers_by_id.entry(cloud_id.to_string()).or_default(); let mut 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.1.push(container) entry.2.push(container)
} }
} }
} }
@ -479,7 +484,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, services))| { .filter_map(|(id, (cloud, info, 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?;
@ -501,6 +506,14 @@ impl Cloud {
.iter() .iter()
.filter_map(|service| service.names.as_ref()?.first().map(String::clone)) .filter_map(|service| service.names.as_ref()?.first().map(String::clone))
.collect(); .collect();
let pinned = (info
.and_then(|info| info.host_config)
.and_then(|host| host.memory)
.unwrap()
% 2)
== 1;
service_ids.push(id.clone()); service_ids.push(id.clone());
Some(( Some((
cloud.created.unwrap_or_default(), cloud.created.unwrap_or_default(),
@ -513,6 +526,7 @@ impl Cloud {
ip: network_info.ip_address.as_ref()?.parse().ok(), ip: network_info.ip_address.as_ref()?.parse().ok(),
workdir, workdir,
services: found_services, services: found_services,
pinned,
}, },
)) ))
}) })
@ -571,4 +585,32 @@ impl Cloud {
.await?; .await?;
Ok(()) Ok(())
} }
pub async fn pin(&self, docker: &mut Docker) -> Result<()> {
// abuse memory limits as editable label
docker
.update_container(
&self.id,
UpdateContainerOptions::<String> {
memory: Some(PHP_MEMORY_LIMIT + 1),
..UpdateContainerOptions::default()
},
)
.await
.into_diagnostic()
}
pub async fn unpin(&self, docker: &mut Docker) -> Result<()> {
// abuse memory limits as editable label
docker
.update_container(
&self.id,
UpdateContainerOptions::<String> {
memory: Some(PHP_MEMORY_LIMIT),
..UpdateContainerOptions::default()
},
)
.await
.into_diagnostic()
}
} }

View file

@ -35,7 +35,7 @@ async fn main() -> Result<()> {
match args { match args {
HazeArgs::Clean => { HazeArgs::Clean => {
let list = Cloud::list(&mut docker, None, &config).await?; let list = Cloud::list(&mut docker, None, &config).await?;
for cloud in list { for cloud in list.into_iter().filter(|cloud| !cloud.pinned) {
if let Err(e) = cloud.destroy(&mut docker).await { if let Err(e) = cloud.destroy(&mut docker).await {
eprintln!("Error while removing cloud: {:#}", e); eprintln!("Error while removing cloud: {:#}", e);
} }
@ -48,17 +48,20 @@ async fn main() -> Result<()> {
let mut services: Vec<_> = cloud.services.iter().map(Service::name).collect(); let mut services: Vec<_> = cloud.services.iter().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 { "" };
match cloud.ip { match cloud.ip {
Some(ip) => println!( Some(ip) => println!(
"Cloud {}, {}, {}, running on http://{}", "Cloud {}{}, {}, {}, running on http://{}",
cloud.id, cloud.id,
pin,
cloud.php.name(), cloud.php.name(),
services, services,
ip ip
), ),
None => println!( None => println!(
"Cloud {}, {}, {}, not running", "Cloud {}{}, {}, {}, not running",
cloud.id, cloud.id,
pin,
cloud.php.name(), cloud.php.name(),
services services
), ),
@ -283,6 +286,14 @@ async fn main() -> Result<()> {
.await?; .await?;
cloud.destroy(&mut docker).await?; cloud.destroy(&mut docker).await?;
} }
HazeArgs::Pin { filter } => {
let cloud = Cloud::get_by_filter(&mut docker, filter, &config).await?;
cloud.pin(&mut docker).await?;
}
HazeArgs::Unpin { filter } => {
let cloud = Cloud::get_by_filter(&mut docker, filter, &config).await?;
cloud.unpin(&mut docker).await?;
}
}; };
Ok(()) Ok(())

View file

@ -11,11 +11,7 @@ pub async fn clear_networks(docker: &Docker) -> Result<()> {
for network in networks { for network in networks {
match network.name.as_deref() { match network.name.as_deref() {
Some(name) if name.starts_with("haze-") => { Some(name) if name.starts_with("haze-") => {
docker docker.remove_network(name).await.ok();
.remove_network(name)
.await
.into_diagnostic()
.wrap_err("Failed to remove docker network")?;
} }
_ => {} _ => {}
} }

View file

@ -26,6 +26,8 @@ pub enum PhpVersion {
Php73Dbg, Php73Dbg,
} }
pub const PHP_MEMORY_LIMIT: i64 = 2 * 1024 * 1024 * 1024;
impl FromStr for PhpVersion { impl FromStr for PhpVersion {
type Err = (); type Err = ();
@ -98,7 +100,7 @@ impl PhpVersion {
network_mode: Some(network.to_string()), network_mode: Some(network.to_string()),
binds: Some(volumes), binds: Some(volumes),
extra_hosts: Some(vec![format!("hazehost:{}", host)]), extra_hosts: Some(vec![format!("hazehost:{}", host)]),
memory: Some(2 * 1024 * 1024 * 1024), memory: Some(PHP_MEMORY_LIMIT),
nano_cpus: Some(2_000_000_000), nano_cpus: Some(2_000_000_000),
..Default::default() ..Default::default()
}), }),