1
0
Fork 0
mirror of https://codeberg.org/icewind/haze.git synced 2026-10-01 08:44:09 +02:00
haze/src/service/slave.rs
Louis Chmn d48a192f3f feat(services): Add slave service to configure global scale slave with
the latest lookup server

Signed-off-by: Louis Chmn <louis@chmn.me>
2026-09-16 23:22:45 +02:00

130 lines
3.9 KiB
Rust

use crate::Result;
use crate::cloud::{Cloud, CloudOptions};
use crate::config::{HazeConfig, ProxyConfig};
use crate::service::lookup_server::GSS_JWT_KEY;
use crate::service::{LookupServer, ServiceTrait};
use bollard::Docker;
use bollard::query_parameters::ListContainersOptions;
use maplit::hashmap;
use miette::{IntoDiagnostic, Report};
use serde_json::Value;
use std::collections::HashMap;
/// Cloud id of the most recently started lookup server.
async fn master_cloud_id(docker: &Docker) -> Result<String> {
let mut lookup_servers: Vec<_> = docker
.list_containers(Some(ListContainersOptions {
all: true,
..Default::default()
}))
.await
.into_diagnostic()?
.iter()
.filter_map(|container| {
let labels = container.labels.as_ref()?;
if labels.get("haze-type").map(String::as_str) != Some(LookupServer.name()) {
return None;
}
let cloud_id = labels.get("haze-cloud-id")?.clone();
Some((container.created.unwrap_or_default(), cloud_id))
})
.collect();
lookup_servers.sort_by(|a, b| a.0.cmp(&b.0).reverse());
lookup_servers.into_iter().map(|(_created, id)| id).next().ok_or_else(|| {
Report::msg("No haze instance with a lookup server is running, start one with `haze start lookup`")
})
}
/// Latest cloud started with a lookup server.
async fn master_instance(docker: &Docker, config: &HazeConfig) -> Result<Cloud> {
let id = master_cloud_id(docker).await?;
Cloud::list(docker, Some(id.clone()), config)
.await?
.into_iter()
.find(|cloud| cloud.id == id)
.ok_or_else(|| Report::msg(format!("Failed to get the address of master instance {id}")))
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct GlobalScaleSlave;
#[async_trait::async_trait]
impl ServiceTrait for GlobalScaleSlave {
fn name(&self) -> &str {
"slave"
}
async fn spawn(
&self,
docker: &Docker,
_cloud_id: &str,
_network: &str,
config: &HazeConfig,
_options: &CloudOptions,
) -> Result<Vec<String>> {
master_instance(docker, config).await?;
Ok(Vec::new())
}
fn apps(&self) -> &'static [&'static str] {
&["globalsiteselector"]
}
fn config(
&self,
_docker: &Docker,
_cloud_id: &str,
_config: &HazeConfig,
) -> Result<HashMap<String, Value>> {
Ok(hashmap! {
"gs.enabled".into() => Value::Bool(true),
"gss.mode".into() => Value::String("slave".into()),
"gss.jwt.key".into() => Value::String(GSS_JWT_KEY.into()),
})
}
async fn post_setup(
&self,
docker: &Docker,
_cloud_id: &str,
config: &HazeConfig,
) -> Result<Vec<Vec<String>>> {
let master = master_instance(docker, config).await?;
let lookup_server = LookupServer
.container_name(&master.id)
.expect("lookup server has a container name");
Ok(vec![
vec![
"occ".into(),
"config:system:set".into(),
"--silent".into(),
"lookup_server".into(),
"--value".into(),
format!("http://{lookup_server}"),
],
vec![
"occ".into(),
"config:system:set".into(),
"--silent".into(),
"gss.master.url".into(),
"--value".into(),
master.address.clone(),
],
vec!["occ".into(), "globalsiteselector:users:update".into()],
])
}
async fn start_message(
&self,
docker: &Docker,
_cloud_id: &str,
_proxy: &ProxyConfig,
) -> Result<Option<String>> {
let master = master_cloud_id(docker).await?;
Ok(Some(format!("Nextcloud was setup as slave of {master}.")))
}
}