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:
parent
0d8f3279ad
commit
81695aee4b
6 changed files with 93 additions and 14 deletions
14
README.md
14
README.md
|
|
@ -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
|
||||||
|
|
|
||||||
14
src/args.rs
14
src/args.rs
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
52
src/cloud.rs
52
src/cloud.rs
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
17
src/main.rs
17
src/main.rs
|
|
@ -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(())
|
||||||
|
|
|
||||||
|
|
@ -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")?;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue