mirror of
https://codeberg.org/icewind/haze.git
synced 2026-06-03 17:14:08 +02:00
allow services and presets to set pre-install config options
This commit is contained in:
parent
90ffcc0a1c
commit
60e797545e
9 changed files with 137 additions and 7 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -669,6 +669,7 @@ dependencies = [
|
|||
"petname",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shell-words",
|
||||
"tar",
|
||||
"termion",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ opener = "0.7.1"
|
|||
toml = "0.8.14"
|
||||
directories-next = "2.0.0"
|
||||
serde = "1.0.203"
|
||||
serde_json = "1.0.117"
|
||||
petname = "2.0.2"
|
||||
reqwest = { version = "0.12.4", default-features = false }
|
||||
tar = "0.4.41"
|
||||
|
|
|
|||
|
|
@ -139,6 +139,13 @@ in {
|
|||
description = "Commands to run post setup when the preset is enabled";
|
||||
default = [];
|
||||
};
|
||||
config = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = format.type;
|
||||
};
|
||||
description = "Configuration options to set before install";
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
|
|
|||
42
src/cloud.rs
42
src/cloud.rs
|
|
@ -1,6 +1,6 @@
|
|||
use crate::config::{HazeConfig, HazeVolumeConfig, Preset};
|
||||
use crate::database::Database;
|
||||
use crate::exec::{exec, exec_tty, ExitCode};
|
||||
use crate::exec::{exec, exec_io, exec_tty, ExitCode};
|
||||
use crate::mapping::{default_mappings, Mapping};
|
||||
use crate::php::{PhpVersion, PHP_MEMORY_LIMIT};
|
||||
use crate::service::Service;
|
||||
|
|
@ -17,7 +17,7 @@ use petname::petname;
|
|||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::fs;
|
||||
use std::io::{stdout, Write};
|
||||
use std::io::{stdout, Cursor, Read, Write, Stdout};
|
||||
use std::iter::Peekable;
|
||||
use std::net::IpAddr;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
|
@ -27,6 +27,7 @@ use tokio::fs::create_dir_all;
|
|||
use tokio::fs::remove_dir_all;
|
||||
use tokio::task::spawn;
|
||||
use tokio::time::sleep;
|
||||
use toml::Value;
|
||||
|
||||
#[derive(Clone, Default, Debug, Eq, PartialEq)]
|
||||
pub struct CloudOptions {
|
||||
|
|
@ -152,6 +153,7 @@ fn test_option_parse() {
|
|||
name: "mypreset".to_string(),
|
||||
commands: Vec::new(),
|
||||
apps: Vec::new(),
|
||||
config: HashMap::default(),
|
||||
}],
|
||||
&mut args
|
||||
)
|
||||
|
|
@ -181,6 +183,7 @@ pub struct Cloud {
|
|||
pub services: Vec<Service>,
|
||||
pub pinned: bool,
|
||||
pub address: String,
|
||||
pub preset_config: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
impl Cloud {
|
||||
|
|
@ -317,6 +320,11 @@ impl Cloud {
|
|||
.await?;
|
||||
containers.extend(service_containers.iter().flatten().cloned());
|
||||
|
||||
let mut preset_config = HashMap::new();
|
||||
for service in &options.services {
|
||||
preset_config.extend(service.config(docker, &id, config)?);
|
||||
}
|
||||
|
||||
env.extend(
|
||||
options
|
||||
.services
|
||||
|
|
@ -427,6 +435,7 @@ impl Cloud {
|
|||
services: options.services,
|
||||
pinned: false,
|
||||
address,
|
||||
preset_config,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -476,6 +485,34 @@ impl Cloud {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn write_file<C: AsRef<[u8]>>(
|
||||
&self,
|
||||
docker: &Docker,
|
||||
path: &str,
|
||||
contents: C,
|
||||
) -> Result<()> {
|
||||
self.exec_io(
|
||||
docker,
|
||||
vec!["tee", path],
|
||||
Vec::<String>::default(),
|
||||
Option::<Stdout>::None,
|
||||
Some(Cursor::new(contents.as_ref())),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn exec_io<S: Into<String>, Env: Into<String>>(
|
||||
&self,
|
||||
docker: &Docker,
|
||||
cmd: Vec<S>,
|
||||
env: Vec<Env>,
|
||||
std_out: Option<impl Write>,
|
||||
std_in: Option<impl Read>,
|
||||
) -> Result<ExitCode> {
|
||||
exec_io(docker, &self.id, "haze", cmd, env, std_out, std_in).await
|
||||
}
|
||||
|
||||
pub async fn occ<'a, S: Into<String> + From<&'a str>, Env: Into<String>>(
|
||||
&self,
|
||||
docker: &Docker,
|
||||
|
|
@ -581,6 +618,7 @@ impl Cloud {
|
|||
services: found_services,
|
||||
pinned,
|
||||
address,
|
||||
preset_config: HashMap::default(),
|
||||
},
|
||||
))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ use camino::Utf8PathBuf;
|
|||
use directories_next::ProjectDirs;
|
||||
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::env::var;
|
||||
use std::fs::read_to_string;
|
||||
use std::net::IpAddr;
|
||||
use toml::Value;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(from = "RawHazeConfig")]
|
||||
|
|
@ -209,6 +211,8 @@ impl HazeConfig {
|
|||
pub struct Preset {
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub config: HashMap<String, Value>,
|
||||
#[serde(default)]
|
||||
pub apps: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub commands: Vec<String>,
|
||||
|
|
|
|||
40
src/exec.rs
40
src/exec.rs
|
|
@ -3,7 +3,7 @@ use bollard::exec::{CreateExecOptions, ResizeExecOptions, StartExecResults};
|
|||
use bollard::Docker;
|
||||
use futures_util::StreamExt;
|
||||
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
||||
use std::io::{stdout, Read, Write};
|
||||
use std::io::{stdout, Read, Stdin, Write};
|
||||
use std::time::Duration;
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::{async_stdin, is_tty, terminal_size};
|
||||
|
|
@ -98,18 +98,40 @@ pub async fn exec_tty<S1: AsRef<str>, S2: Into<String>, Env: Into<String>>(
|
|||
}
|
||||
|
||||
pub async fn exec<S1: AsRef<str>, S2: Into<String>, Env: Into<String>>(
|
||||
docker: &Docker,
|
||||
container: S1,
|
||||
user: &str,
|
||||
cmd: Vec<S2>,
|
||||
env: Vec<Env>,
|
||||
std_out: Option<impl Write>,
|
||||
) -> Result<ExitCode> {
|
||||
exec_io(
|
||||
docker,
|
||||
container,
|
||||
user,
|
||||
cmd,
|
||||
env,
|
||||
std_out,
|
||||
Option::<Stdin>::None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn exec_io<S1: AsRef<str>, S2: Into<String>, Env: Into<String>>(
|
||||
docker: &Docker,
|
||||
container: S1,
|
||||
user: &str,
|
||||
cmd: Vec<S2>,
|
||||
env: Vec<Env>,
|
||||
mut std_out: Option<impl Write>,
|
||||
std_in: Option<impl Read>,
|
||||
) -> Result<ExitCode> {
|
||||
let cmd = cmd.into_iter().map(S2::into).collect();
|
||||
let env = env.into_iter().map(Env::into).collect();
|
||||
let config = CreateExecOptions {
|
||||
cmd: Some(cmd),
|
||||
user: Some(user.to_string()),
|
||||
attach_stdin: Some(std_in.is_some()),
|
||||
attach_stdout: Some(true),
|
||||
attach_stderr: Some(true),
|
||||
env: Some(env),
|
||||
|
|
@ -121,12 +143,26 @@ pub async fn exec<S1: AsRef<str>, S2: Into<String>, Env: Into<String>>(
|
|||
.await
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to setup exec")?;
|
||||
if let StartExecResults::Attached { mut output, .. } = docker
|
||||
if let StartExecResults::Attached {
|
||||
mut output,
|
||||
mut input,
|
||||
} = docker
|
||||
.start_exec(&message.id, None)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to start exec")?
|
||||
{
|
||||
if let Some(mut std_in) = std_in {
|
||||
let mut buff = [0; 4 * 1024];
|
||||
loop {
|
||||
let bytes = std_in.read(&mut buff).into_diagnostic()?;
|
||||
if bytes == 0 {
|
||||
break;
|
||||
}
|
||||
input.write_all(&buff[0..bytes]).await.into_diagnostic()?;
|
||||
}
|
||||
input.shutdown().await.into_diagnostic()?;
|
||||
}
|
||||
while let Some(Ok(line)) = output.next().await {
|
||||
if let Some(std_out) = &mut std_out {
|
||||
write!(std_out, "{}", line).into_diagnostic()?;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ pub async fn pull_image(docker: &Docker, image: &str) -> Result<()> {
|
|||
let info: CreateImageInfo = info
|
||||
.into_diagnostic()
|
||||
.wrap_err_with(|| format!("Error while pulling image {}", image))?;
|
||||
// dbg!(&info);
|
||||
if let (Some(id), Some(status), Some(progress)) = (info.id, info.status, info.progress)
|
||||
{
|
||||
match bars.get(&id) {
|
||||
|
|
|
|||
22
src/main.rs
22
src/main.rs
|
|
@ -169,6 +169,17 @@ async fn main() -> Result<()> {
|
|||
let cloud = Cloud::create(&docker, options, &config).await?;
|
||||
println!("Waiting for servers to start");
|
||||
cloud.wait_for_start(&docker).await?;
|
||||
|
||||
if !cloud.preset_config.is_empty() {
|
||||
println!("Writing preset config");
|
||||
let encoded_preset_config =
|
||||
serde_json::to_string(&cloud.preset_config).into_diagnostic()?;
|
||||
cloud
|
||||
.write_file(&docker, "config/preset.config.json", encoded_preset_config)
|
||||
.await?;
|
||||
cloud.write_file(&docker, "config/preset.config.php", "<?php $CONFIG=json_decode(file_get_contents(__DIR__ . '/preset.config.json'), true);").await?;
|
||||
}
|
||||
|
||||
println!("Installing");
|
||||
if let Err(e) = cloud
|
||||
.exec(
|
||||
|
|
@ -370,6 +381,17 @@ async fn setup(docker: &Docker, options: CloudOptions, config: &HazeConfig) -> R
|
|||
if config.auto_setup.enabled {
|
||||
println!("Waiting for servers to start");
|
||||
cloud.wait_for_start(docker).await?;
|
||||
|
||||
if !cloud.preset_config.is_empty() {
|
||||
println!("Writing preset config");
|
||||
let encoded_preset_config =
|
||||
serde_json::to_string(&cloud.preset_config).into_diagnostic()?;
|
||||
cloud
|
||||
.write_file(docker, "config/preset.config.json", encoded_preset_config)
|
||||
.await?;
|
||||
cloud.write_file(docker, "config/preset.config.php", "<?php $CONFIG=json_decode(file_get_contents(__DIR__ . '/preset.config.json'), true);").await?;
|
||||
}
|
||||
|
||||
println!(
|
||||
"Installing with username {} and password {}",
|
||||
config.auto_setup.username, config.auto_setup.password
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ mod dav;
|
|||
mod imaginary;
|
||||
mod kaspersky;
|
||||
mod ldap;
|
||||
mod mail;
|
||||
mod objectstore;
|
||||
mod oc;
|
||||
mod office;
|
||||
|
|
@ -10,7 +11,6 @@ mod onlyoffice;
|
|||
mod push;
|
||||
mod sftp;
|
||||
mod smb;
|
||||
mod mail;
|
||||
|
||||
use crate::config::{HazeConfig, Preset};
|
||||
pub use crate::service::clam::{ClamIcap, ClamIcapTls};
|
||||
|
|
@ -18,6 +18,7 @@ use crate::service::dav::Dav;
|
|||
use crate::service::imaginary::Imaginary;
|
||||
use crate::service::kaspersky::{Kaspersky, KasperskyIcap};
|
||||
pub use crate::service::ldap::{Ldap, LdapAdmin};
|
||||
use crate::service::mail::Mail;
|
||||
pub use crate::service::objectstore::ObjectStore;
|
||||
use crate::service::oc::Oc;
|
||||
pub use crate::service::office::Office;
|
||||
|
|
@ -29,10 +30,11 @@ use bollard::models::ContainerState;
|
|||
use bollard::Docker;
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
use std::time::Duration;
|
||||
use tokio::time::{sleep, timeout};
|
||||
use crate::service::mail::Mail;
|
||||
use toml::Value;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
#[enum_dispatch(Service)]
|
||||
|
|
@ -82,6 +84,15 @@ pub trait ServiceTrait {
|
|||
&[]
|
||||
}
|
||||
|
||||
fn config(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
_config: &HazeConfig,
|
||||
) -> Result<HashMap<String, Value>> {
|
||||
Ok(HashMap::default())
|
||||
}
|
||||
|
||||
async fn post_setup(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
|
|
@ -246,6 +257,17 @@ impl ServiceTrait for PresetService {
|
|||
self.0.as_str()
|
||||
}
|
||||
|
||||
fn config(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
_cloud_id: &str,
|
||||
config: &HazeConfig,
|
||||
) -> Result<HashMap<String, Value>> {
|
||||
let preset =
|
||||
get_preset(&config.preset, &self.0).ok_or_else(|| Report::msg("invalid preset"))?;
|
||||
Ok(preset.config.clone())
|
||||
}
|
||||
|
||||
async fn post_setup(
|
||||
&self,
|
||||
_docker: &Docker,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue