1
0
Fork 0
mirror of https://codeberg.org/icewind/haze.git synced 2026-06-03 09:04:12 +02:00
This commit is contained in:
Robin Appelman 2021-03-11 22:39:43 +01:00
commit afb7d0ae6f
6 changed files with 1590 additions and 3 deletions

1243
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,10 @@ version = "0.1.0"
authors = ["Robin Appelman <robin@icewind.nl>"] authors = ["Robin Appelman <robin@icewind.nl>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
bollard = "0.10"
color-eyre = "0.5"
min-id = "0.1"
maplit = "1"
camino = "1"
tokio = { version = "1" , features = ["fs", "macros"] }

309
src/cloud.rs Normal file
View file

@ -0,0 +1,309 @@
use crate::config::HazeConfig;
use bollard::container::{Config, CreateContainerOptions};
use bollard::models::HostConfig;
use bollard::network::CreateNetworkOptions;
use bollard::Docker;
use camino::Utf8Path;
use color_eyre::{eyre::WrapErr, Report, Result};
use min_id::generate_id;
use tokio::fs::{create_dir_all, write};
pub enum Database {
Sqlite,
Mysql,
Mysql80,
Mysql57,
Mysql56,
MariaDB,
MariaDB101,
MariaDB102,
MariaDB103,
MariaDB104,
MariaDB105,
Postgres,
Postgres9,
Postgres10,
Postgres11,
Postgres12,
Postgres13,
}
impl Default for Database {
fn default() -> Self {
Database::Sqlite
}
}
impl Database {
pub fn image(&self) -> &'static str {
match self {
Database::Sqlite => "",
Database::Mysql => "mysql",
Database::Mysql80 => "mysql:8",
Database::Mysql57 => "mysql:5.7",
Database::Mysql56 => "mysql:5.6",
Database::MariaDB => "mariadb",
Database::MariaDB101 => "mariadb:10.1",
Database::MariaDB102 => "mariadb:10.2",
Database::MariaDB103 => "mariadb:10.3",
Database::MariaDB104 => "mariadb:10.4",
Database::MariaDB105 => "mariadb:10.5",
Database::Postgres => "postgresql",
Database::Postgres9 => "postgresql:9",
Database::Postgres10 => "postgresql:10",
Database::Postgres11 => "postgresql:11",
Database::Postgres12 => "postgresql:12",
Database::Postgres13 => "postgresql:13",
}
}
pub fn name(&self) -> &'static str {
match self {
Database::Sqlite => "sqlite",
Database::Mysql
| Database::Mysql80
| Database::Mysql57
| Database::Mysql56
| Database::MariaDB
| Database::MariaDB101
| Database::MariaDB102
| Database::MariaDB103
| Database::MariaDB104
| Database::MariaDB105 => "mysql",
Database::Postgres
| Database::Postgres9
| Database::Postgres10
| Database::Postgres11
| Database::Postgres12
| Database::Postgres13 => "pgsql",
}
}
pub fn env(&self) -> Vec<&'static str> {
match self {
Database::Sqlite => Vec::new(),
Database::Mysql
| Database::Mysql80
| Database::Mysql57
| Database::Mysql56
| Database::MariaDB
| Database::MariaDB101
| Database::MariaDB102
| Database::MariaDB103
| Database::MariaDB104
| Database::MariaDB105 => vec![
"MYSQL_ROOT_PASSWORD=haze",
"MYSQL_PASSWORD=haze",
"MYSQL_USER=haze",
"MYSQL_DATABASE=haze",
],
Database::Postgres
| Database::Postgres9
| Database::Postgres10
| Database::Postgres11
| Database::Postgres12
| Database::Postgres13 => vec![
"POSTGRES_PASSWORD=haze",
"POSTGRES_USER=haze",
"POSTGRES_DATABASE=haze",
],
}
}
pub async fn spawn(
&self,
docker: &mut Docker,
cloud_id: &str,
network: &str,
) -> Result<Option<String>> {
if matches!(self, Database::Sqlite) {
return Ok(None);
}
let options = Some(CreateContainerOptions {
name: format!("{}-db", cloud_id),
});
let config = Config {
image: Some(self.image()),
env: Some(self.env()),
host_config: Some(HostConfig {
network_mode: Some(network.to_string()),
..Default::default()
}),
..Default::default()
};
Ok(Some(docker.create_container(options, config).await?.id))
}
}
pub enum PhpVersion {
Latest,
// Php80,
Php74,
// Php73,
}
impl PhpVersion {
fn image(&self) -> &'static str {
// for now only 7.4
match self {
PhpVersion::Latest => "icewind1991/nextcloud-dev:7",
PhpVersion::Php74 => "icewind1991/nextcloud-dev:7",
}
}
async fn spawn(
&self,
docker: &mut Docker,
id: &str,
env: Vec<String>,
network: &str,
links: Vec<String>,
volumes: Vec<String>,
) -> Result<String> {
let options = Some(CreateContainerOptions {
name: id.to_string(),
});
let config = Config {
image: Some(self.image().to_string()),
env: Some(env),
host_config: Some(HostConfig {
network_mode: Some(network.to_string()),
links: Some(links),
binds: Some(volumes),
..Default::default()
}),
..Default::default()
};
Ok(docker.create_container(options, config).await?.id)
}
}
impl Default for PhpVersion {
fn default() -> Self {
PhpVersion::Latest
}
}
#[derive(Default)]
pub struct CloudOptions {
db: Database,
php: PhpVersion,
}
pub struct Cloud {
id: String,
network: String,
containers: Vec<String>,
}
impl Cloud {
pub async fn create(
docker: &mut Docker,
options: CloudOptions,
config: HazeConfig,
) -> Result<Self> {
let id = generate_id();
setup_workdir(&config.work_dir, &id)
.await
.wrap_err("Failed to setup work directories")?;
let network = docker
.create_network(CreateNetworkOptions {
name: format!("cloud-{}", id),
..Default::default()
})
.await?
.id
.ok_or(Report::msg("No network id in response"))
.wrap_err("Failed to create network")?;
let mut containers = Vec::new();
let mut links = Vec::new();
let mut env = vec!["PHP_IDE_CONFIG=serverName=haze".to_string()];
let volumes = vec![
format!("{}:/var/www/html", config.sources_root),
format!("{}/{}/data:/var/www/html/data", config.work_dir, id),
format!("{}/{}/config:/var/www/html/config", config.work_dir, id),
format!(
"{}/{}/data-autotest:/var/www/html/data-autotest",
config.work_dir, id
),
format!(
"{}/{}/skeleton:/var/www/html/core/skeleton",
config.work_dir, id
),
format!(
"{}/{}/skeleton/welcome.txt:/var/www/html/core/skeleton/welcome.txt:ro",
config.work_dir, id
),
format!(
"{}/{}/integration/vendor:/var/www/html/build/integration/vendor",
config.work_dir, id
),
format!(
"{}/{}/integration/work:/var/www/html/build/integration/work",
config.work_dir, id
),
format!(
"{}/{}/integration/output:/var/www/html/build/integration/output",
config.work_dir, id
),
format!(
"{}/{}/integration/composer.lock:/var/www/html/build/integration/composer.lock",
config.work_dir, id
),
format!(
"{}/composer/cache:/var/www/.composer/cache",
config.work_dir
),
format!(
"{}/phpunit-cache:/var/www/html/tests/.phpunit.results.cache",
config.work_dir
),
];
if let Some(db) = options
.db
.spawn(docker, &id, &network)
.await
.wrap_err("Failed to start database")?
{
containers.push(db);
links.push(format!("{}-db:{}", id, options.db.name()));
env.push(format!("SQL={}", options.db.name()));
}
let container = options
.php
.spawn(docker, &id, env, &network, links, volumes)
.await
.wrap_err("Failed to start php container")?;
containers.push(container);
Ok(Cloud {
id,
network,
containers,
})
}
}
async fn setup_workdir(base: &Utf8Path, id: &str) -> Result<()> {
let workdir = base.join(id);
create_dir_all(workdir.join("data")).await?;
create_dir_all(workdir.join("config")).await?;
create_dir_all(workdir.join("data-autotest")).await?;
create_dir_all(workdir.join("skeleton")).await?;
create_dir_all(workdir.join("integration/output")).await?;
create_dir_all(workdir.join("integration/work")).await?;
create_dir_all(workdir.join("integration/vendor")).await?;
write(workdir.join("integration/composer.lock"), "").await?;
write(workdir.join("config/CAN_INSTALL"), "").await?;
write(workdir.join("phpunit-cache"), "").await?;
create_dir_all(base.join("composer/cache")).await?;
Ok(())
}

6
src/config.rs Normal file
View file

@ -0,0 +1,6 @@
use camino::Utf8PathBuf;
pub struct HazeConfig {
pub sources_root: Utf8PathBuf,
pub work_dir: Utf8PathBuf,
}

13
src/docker.rs Normal file
View file

@ -0,0 +1,13 @@
use color_eyre::Result;
pub struct Docker {
handle: bollard::Docker,
}
impl Docker {
pub fn new() -> Result<Self> {
Ok(Docker {
handle: bollard::Docker::connect_with_local_defaults()?,
})
}
}

View file

@ -1,3 +1,15 @@
use crate::config::HazeConfig;
use camino::Utf8Path;
use color_eyre::{eyre::WrapErr, Report, Result};
use std::fs::create_dir_all;
mod cloud;
mod config;
mod docker;
fn main() { fn main() {
println!("Hello, world!"); let config = HazeConfig {
sources_root: "/srv/http/owncloud".into(),
work_dir: "/tmp/oc-docket".into(),
};
} }