mirror of
https://codeberg.org/icewind/haze.git
synced 2026-06-03 17:14:08 +02:00
allow configuring additional app directories and add a writable app directory
fixes #15
This commit is contained in:
parent
862d33b017
commit
b7ea4e9760
6 changed files with 130 additions and 34 deletions
|
|
@ -303,6 +303,7 @@ options
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
sources_root = "/path/to/sources" # path of the nextcloud sources. required
|
sources_root = "/path/to/sources" # path of the nextcloud sources. required
|
||||||
|
app_directories = ["/path/to/sources/more_app"] # paths to additional app directories.
|
||||||
work_dir = "/path/to/temp/dir" # path to temporary directory. optional, defaults to "/tmp/haze"
|
work_dir = "/path/to/temp/dir" # path to temporary directory. optional, defaults to "/tmp/haze"
|
||||||
|
|
||||||
[auto_setup] # optional
|
[auto_setup] # optional
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,12 @@
|
||||||
<?php $CONFIG=[
|
<?php
|
||||||
|
|
||||||
|
$extra_config = [];
|
||||||
|
if (file_exists(__DIR__ . '/nextcloud.json')) {
|
||||||
|
$extra_config = json_decode(file_get_contents(__DIR__ . '/nextcloud.json'), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$CONFIG = array_merge_recursive($extra_config, [
|
||||||
'debug' => true,
|
'debug' => true,
|
||||||
'appstoreenabled' => false,
|
'appstoreenabled' => false,
|
||||||
'memcache.local' => '\\OC\\Memcache\\APCu',
|
'memcache.local' => '\\OC\\Memcache\\APCu',
|
||||||
|
|
@ -9,4 +17,4 @@
|
||||||
'profiling.secret' => 'haze',
|
'profiling.secret' => 'haze',
|
||||||
'profiling.path' => '/tmp/profiling',
|
'profiling.path' => '/tmp/profiling',
|
||||||
//PLACEHOLDER
|
//PLACEHOLDER
|
||||||
];
|
]);
|
||||||
|
|
|
||||||
|
|
@ -505,6 +505,7 @@ impl SubCommand for HazeCommand {
|
||||||
fn test_arg_parse() {
|
fn test_arg_parse() {
|
||||||
let config = HazeConfig {
|
let config = HazeConfig {
|
||||||
sources_root: Default::default(),
|
sources_root: Default::default(),
|
||||||
|
app_directories: Default::default(),
|
||||||
work_dir: Default::default(),
|
work_dir: Default::default(),
|
||||||
auto_setup: Default::default(),
|
auto_setup: Default::default(),
|
||||||
volume: vec![],
|
volume: vec![],
|
||||||
|
|
|
||||||
69
src/cloud.rs
69
src/cloud.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::config::{HazeConfig, HazeVolumeConfig};
|
use crate::config::{HazeConfig, HazeVolumeConfig};
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
use crate::exec::{exec, exec_io, exec_tty, ExitCode};
|
use crate::exec::{exec, exec_io, exec_tty, ExitCode};
|
||||||
use crate::mapping::{default_mappings, Mapping};
|
use crate::mapping::{for_config, Mapping};
|
||||||
use crate::php::{PhpVersion, PHP_MEMORY_LIMIT};
|
use crate::php::{PhpVersion, PHP_MEMORY_LIMIT};
|
||||||
use crate::service::Service;
|
use crate::service::Service;
|
||||||
use crate::service::ServiceTrait;
|
use crate::service::ServiceTrait;
|
||||||
|
|
@ -15,14 +15,14 @@ use flate2::read::GzDecoder;
|
||||||
use futures_util::future::try_join_all;
|
use futures_util::future::try_join_all;
|
||||||
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
||||||
use petname::petname;
|
use petname::petname;
|
||||||
use serde_json::Value;
|
use serde_json::{Map, Value};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::read_to_string;
|
use std::fs::{read_to_string, write};
|
||||||
use std::io::{stdout, Cursor, Read, Stdout, Write};
|
use std::io::{stdout, Cursor, Read, Stdout, Write};
|
||||||
use std::iter::Peekable;
|
use std::iter::{once, Peekable};
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
@ -286,11 +286,8 @@ impl Cloud {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
let mappings = config
|
|
||||||
.volume
|
let mappings = for_config(config)
|
||||||
.iter()
|
|
||||||
.map(Mapping::from)
|
|
||||||
.chain(default_mappings())
|
|
||||||
.chain(app_volumes.iter().map(Mapping::from))
|
.chain(app_volumes.iter().map(Mapping::from))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
for mapping in &mappings {
|
for mapping in &mappings {
|
||||||
|
|
@ -300,6 +297,48 @@ impl Cloud {
|
||||||
.wrap_err_with(|| format!("Failed to setup work directory {}", mapping.source))?;
|
.wrap_err_with(|| format!("Failed to setup work directory {}", mapping.source))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut nc_config = Value::Object(Map::new());
|
||||||
|
nc_config["apps_paths"] = Value::Array(
|
||||||
|
once("apps")
|
||||||
|
.chain(
|
||||||
|
config
|
||||||
|
.app_directories
|
||||||
|
.iter()
|
||||||
|
.filter_map(|dir| dir.file_name()),
|
||||||
|
)
|
||||||
|
.map(|name| {
|
||||||
|
[
|
||||||
|
(
|
||||||
|
String::from("path"),
|
||||||
|
Value::from(format!("/var/www/html/{}", name)),
|
||||||
|
),
|
||||||
|
(String::from("url"), Value::from(format!("/{}", name))),
|
||||||
|
(String::from("writable"), Value::from(false)),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.chain(once(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
String::from("path"),
|
||||||
|
Value::from("/var/www/html/store_apps"),
|
||||||
|
),
|
||||||
|
(String::from("url"), Value::from("/store_apps")),
|
||||||
|
(String::from("writable"), Value::from(true)),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
write(
|
||||||
|
workdir.join("config/nextcloud.json"),
|
||||||
|
serde_json::to_string_pretty(&nc_config).unwrap(),
|
||||||
|
)
|
||||||
|
.into_diagnostic()
|
||||||
|
.wrap_err("Failed to write config json")?;
|
||||||
|
|
||||||
let network = docker
|
let network = docker
|
||||||
.create_network(NetworkCreateRequest {
|
.create_network(NetworkCreateRequest {
|
||||||
name: id.clone(),
|
name: id.clone(),
|
||||||
|
|
@ -500,10 +539,7 @@ impl Cloud {
|
||||||
pub async fn destroy(self, docker: &Docker) -> Result<()> {
|
pub async fn destroy(self, docker: &Docker) -> Result<()> {
|
||||||
for container in self.containers {
|
for container in self.containers {
|
||||||
docker
|
docker
|
||||||
.kill_container(
|
.kill_container(container.trim_start_matches('/'), None)
|
||||||
container.trim_start_matches('/'),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.into_diagnostic()
|
.into_diagnostic()
|
||||||
.wrap_err("Failed to kill container")?;
|
.wrap_err("Failed to kill container")?;
|
||||||
|
|
@ -802,12 +838,7 @@ impl Cloud {
|
||||||
format!("/var/www/html/{path}").into()
|
format!("/var/www/html/{path}").into()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut mappings = config
|
let mut mappings = for_config(config).collect::<Vec<_>>();
|
||||||
.volume
|
|
||||||
.iter()
|
|
||||||
.map(Mapping::from)
|
|
||||||
.chain(default_mappings())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
mappings.sort_by_key(|mapping| usize::MAX - mapping.target.as_str().len());
|
mappings.sort_by_key(|mapping| usize::MAX - mapping.target.as_str().len());
|
||||||
|
|
||||||
for mapping in mappings {
|
for mapping in mappings {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use miette::{IntoDiagnostic, Report, Result, WrapErr};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::env::var;
|
use std::env::home_dir;
|
||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use toml::Value;
|
use toml::Value;
|
||||||
|
|
@ -13,6 +13,7 @@ use toml::Value;
|
||||||
#[serde(from = "RawHazeConfig")]
|
#[serde(from = "RawHazeConfig")]
|
||||||
pub struct HazeConfig {
|
pub struct HazeConfig {
|
||||||
pub sources_root: Utf8PathBuf,
|
pub sources_root: Utf8PathBuf,
|
||||||
|
pub app_directories: Vec<Utf8PathBuf>,
|
||||||
pub work_dir: Utf8PathBuf,
|
pub work_dir: Utf8PathBuf,
|
||||||
pub auto_setup: HazeAutoSetupConfig,
|
pub auto_setup: HazeAutoSetupConfig,
|
||||||
pub volume: Vec<HazeVolumeConfig>,
|
pub volume: Vec<HazeVolumeConfig>,
|
||||||
|
|
@ -27,6 +28,8 @@ pub struct RawHazeConfig {
|
||||||
#[serde(default = "default_work_dir")]
|
#[serde(default = "default_work_dir")]
|
||||||
pub work_dir: Utf8PathBuf,
|
pub work_dir: Utf8PathBuf,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub app_directories: Vec<Utf8PathBuf>,
|
||||||
|
#[serde(default)]
|
||||||
pub auto_setup: HazeAutoSetupConfig,
|
pub auto_setup: HazeAutoSetupConfig,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub volume: Vec<HazeVolumeConfig>,
|
pub volume: Vec<HazeVolumeConfig>,
|
||||||
|
|
@ -42,7 +45,11 @@ impl From<RawHazeConfig> for HazeConfig {
|
||||||
fn from(raw: RawHazeConfig) -> Self {
|
fn from(raw: RawHazeConfig) -> Self {
|
||||||
fn normalize_path(path: Utf8PathBuf) -> Utf8PathBuf {
|
fn normalize_path(path: Utf8PathBuf) -> Utf8PathBuf {
|
||||||
if path.starts_with("~") {
|
if path.starts_with("~") {
|
||||||
let home = var("HOME").expect("HOME not set");
|
let home = home_dir().expect("can't detect home directory");
|
||||||
|
let home = home
|
||||||
|
.into_os_string()
|
||||||
|
.into_string()
|
||||||
|
.expect("non-utf8 home directory");
|
||||||
format!("{}{}", home, &path.as_str()[1..]).into()
|
format!("{}{}", home, &path.as_str()[1..]).into()
|
||||||
} else {
|
} else {
|
||||||
path
|
path
|
||||||
|
|
@ -51,6 +58,11 @@ impl From<RawHazeConfig> for HazeConfig {
|
||||||
|
|
||||||
HazeConfig {
|
HazeConfig {
|
||||||
sources_root: normalize_path(raw.sources_root),
|
sources_root: normalize_path(raw.sources_root),
|
||||||
|
app_directories: raw
|
||||||
|
.app_directories
|
||||||
|
.into_iter()
|
||||||
|
.map(normalize_path)
|
||||||
|
.collect(),
|
||||||
work_dir: normalize_path(raw.work_dir),
|
work_dir: normalize_path(raw.work_dir),
|
||||||
auto_setup: raw.auto_setup,
|
auto_setup: raw.auto_setup,
|
||||||
volume: raw.volume,
|
volume: raw.volume,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
use crate::config::{HazeConfig, HazeVolumeConfig};
|
use crate::config::{HazeConfig, HazeVolumeConfig};
|
||||||
use camino::{Utf8Path, Utf8PathBuf};
|
use camino::{Utf8Path, Utf8PathBuf};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
|
use std::borrow::Cow;
|
||||||
use tokio::fs::{create_dir_all, write};
|
use tokio::fs::{create_dir_all, write};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Mapping<'a> {
|
pub struct Mapping<'a> {
|
||||||
source_type: MappingSourceType,
|
source_type: MappingSourceType,
|
||||||
pub source: &'a Utf8Path,
|
pub source: Cow<'a, Utf8Path>,
|
||||||
pub target: &'a Utf8Path,
|
pub target: Cow<'a, Utf8Path>,
|
||||||
mapping_type: MappingType,
|
mapping_type: MappingType,
|
||||||
read_only: bool,
|
read_only: bool,
|
||||||
map: bool,
|
map: bool,
|
||||||
|
|
@ -23,6 +24,26 @@ impl<'a> Mapping<'a> {
|
||||||
where
|
where
|
||||||
Target: Into<&'a Utf8Path>,
|
Target: Into<&'a Utf8Path>,
|
||||||
Source: Into<&'a Utf8Path>,
|
Source: Into<&'a Utf8Path>,
|
||||||
|
{
|
||||||
|
Mapping {
|
||||||
|
source_type,
|
||||||
|
source: Cow::Borrowed(source.into()),
|
||||||
|
target: Cow::Borrowed(target.into()),
|
||||||
|
mapping_type: MappingType::Folder,
|
||||||
|
read_only: false,
|
||||||
|
map: true,
|
||||||
|
create: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn owned<Source, Target>(
|
||||||
|
source_type: MappingSourceType,
|
||||||
|
source: Source,
|
||||||
|
target: Target,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
Target: Into<Cow<'a, Utf8Path>>,
|
||||||
|
Source: Into<Cow<'a, Utf8Path>>,
|
||||||
{
|
{
|
||||||
Mapping {
|
Mapping {
|
||||||
source_type,
|
source_type,
|
||||||
|
|
@ -65,10 +86,10 @@ impl<'a> Mapping<'a> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let source = match self.source_type {
|
let source = match self.source_type {
|
||||||
MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source),
|
MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source.as_ref()),
|
||||||
MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source),
|
MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source.as_ref()),
|
||||||
MappingSourceType::Sources => return Ok(()),
|
MappingSourceType::Sources => return Ok(()),
|
||||||
MappingSourceType::Absolute => self.source.into(),
|
MappingSourceType::Absolute => self.source.as_ref().into(),
|
||||||
};
|
};
|
||||||
match self.mapping_type {
|
match self.mapping_type {
|
||||||
MappingType::Folder => create_dir_all(source).await.into_diagnostic()?,
|
MappingType::Folder => create_dir_all(source).await.into_diagnostic()?,
|
||||||
|
|
@ -80,10 +101,10 @@ impl<'a> Mapping<'a> {
|
||||||
|
|
||||||
pub fn source(&self, id: &str, config: &HazeConfig, source_root: &Utf8Path) -> Utf8PathBuf {
|
pub fn source(&self, id: &str, config: &HazeConfig, source_root: &Utf8Path) -> Utf8PathBuf {
|
||||||
match self.source_type {
|
match self.source_type {
|
||||||
MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source),
|
MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source.as_ref()),
|
||||||
MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source),
|
MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source.as_ref()),
|
||||||
MappingSourceType::Sources => source_root.join(self.source),
|
MappingSourceType::Sources => source_root.join(self.source.as_ref()),
|
||||||
MappingSourceType::Absolute => self.source.into(),
|
MappingSourceType::Absolute => self.source.as_ref().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,6 +133,7 @@ pub fn default_mappings<'a>() -> impl IntoIterator<Item = Mapping<'a>> {
|
||||||
Mapping::new(Sources, "", "/var/www/html"),
|
Mapping::new(Sources, "", "/var/www/html"),
|
||||||
Mapping::new(WorkDir, "data", "/var/www/html/data"),
|
Mapping::new(WorkDir, "data", "/var/www/html/data"),
|
||||||
Mapping::new(WorkDir, "config", "/var/www/html/config"),
|
Mapping::new(WorkDir, "config", "/var/www/html/config"),
|
||||||
|
Mapping::new(WorkDir, "store_app", "/var/www/html/store_app"),
|
||||||
Mapping::new(WorkDir, "data-autotest", "/var/www/html/data-autotest"),
|
Mapping::new(WorkDir, "data-autotest", "/var/www/html/data-autotest"),
|
||||||
Mapping::new(WorkDir, "skeleton", "/var/www/html/core/skeleton"),
|
Mapping::new(WorkDir, "skeleton", "/var/www/html/core/skeleton"),
|
||||||
Mapping::new(
|
Mapping::new(
|
||||||
|
|
@ -168,9 +190,30 @@ pub fn default_mappings<'a>() -> impl IntoIterator<Item = Mapping<'a>> {
|
||||||
Mapping::new(WorkDir, "profiling", "/tmp/profiling"),
|
Mapping::new(WorkDir, "profiling", "/tmp/profiling"),
|
||||||
Mapping::new(WorkDir, "php-config", "/config"),
|
Mapping::new(WorkDir, "php-config", "/config"),
|
||||||
];
|
];
|
||||||
|
|
||||||
IntoIterator::into_iter(mappings)
|
IntoIterator::into_iter(mappings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_config<'a>(config: &'a HazeConfig) -> impl Iterator<Item = Mapping<'a>> {
|
||||||
|
let app_dir_mappings = config.app_directories.iter().map(|dir| {
|
||||||
|
Mapping::owned(
|
||||||
|
MappingSourceType::Absolute,
|
||||||
|
dir.as_path(),
|
||||||
|
Cow::Owned(Utf8PathBuf::from(format!(
|
||||||
|
"/var/www/html/{}",
|
||||||
|
dir.file_name().unwrap()
|
||||||
|
))),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
config
|
||||||
|
.volume
|
||||||
|
.iter()
|
||||||
|
.map(Mapping::from)
|
||||||
|
.chain(app_dir_mappings)
|
||||||
|
.chain(default_mappings())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum MappingSourceType {
|
pub enum MappingSourceType {
|
||||||
Sources,
|
Sources,
|
||||||
|
|
@ -194,8 +237,8 @@ impl<'a> From<&'a HazeVolumeConfig> for Mapping<'a> {
|
||||||
};
|
};
|
||||||
Mapping {
|
Mapping {
|
||||||
source_type: MappingSourceType::Absolute,
|
source_type: MappingSourceType::Absolute,
|
||||||
source: config.source.as_path(),
|
source: Cow::Borrowed(config.source.as_path()),
|
||||||
target: config.target.as_path(),
|
target: Cow::Borrowed(config.target.as_path()),
|
||||||
mapping_type: ty,
|
mapping_type: ty,
|
||||||
read_only: config.read_only,
|
read_only: config.read_only,
|
||||||
map: true,
|
map: true,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue