1
0
Fork 0
mirror of https://codeberg.org/icewind/haze.git synced 2026-06-03 17:14:08 +02:00

basic subcommands

This commit is contained in:
Robin Appelman 2021-03-12 20:04:15 +01:00
commit da1e184e42
3 changed files with 192 additions and 41 deletions

View file

@ -1,12 +1,11 @@
use color_eyre::{eyre::WrapErr, Report, Result}; use color_eyre::{Report, Result};
use std::env::Args;
use std::str::FromStr; use std::str::FromStr;
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct HazeArgs { pub struct HazeArgs {
id: Option<String>, pub id: Option<String>,
command: Command, pub command: HazeCommand,
options: Vec<String>, pub options: Vec<String>,
} }
impl HazeArgs { impl HazeArgs {
@ -24,11 +23,11 @@ impl HazeArgs {
if let Some(sub) = args.next() { if let Some(sub) = args.next() {
(Some(sub_or_id.to_string()), sub.as_ref().parse()?) (Some(sub_or_id.to_string()), sub.as_ref().parse()?)
} else { } else {
(Some(sub_or_id.to_string()), Command::List) (Some(sub_or_id.to_string()), HazeCommand::List)
} }
} }
} }
None => (None, Command::List), None => (None, HazeCommand::List),
}; };
let options = args.map(|s| s.to_string()).collect(); let options = args.map(|s| s.to_string()).collect();
Ok(HazeArgs { Ok(HazeArgs {
@ -40,7 +39,7 @@ impl HazeArgs {
} }
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Command { pub enum HazeCommand {
List, List,
Start, Start,
Stop, Stop,
@ -48,20 +47,22 @@ pub enum Command {
Exec, Exec,
Occ, Occ,
Db, Db,
Clean,
} }
impl FromStr for Command { impl FromStr for HazeCommand {
type Err = Report; type Err = Report;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"list" => Ok(Command::List), "list" => Ok(HazeCommand::List),
"start" => Ok(Command::Start), "start" => Ok(HazeCommand::Start),
"stop" => Ok(Command::Stop), "stop" => Ok(HazeCommand::Stop),
"test" => Ok(Command::Test), "test" => Ok(HazeCommand::Test),
"exec" => Ok(Command::Exec), "exec" => Ok(HazeCommand::Exec),
"occ" => Ok(Command::Occ), "occ" => Ok(HazeCommand::Occ),
"db" => Ok(Command::Db), "db" => Ok(HazeCommand::Db),
"clean" => Ok(HazeCommand::Clean),
_ => Err(Report::msg(format!("Unknown command: {}", s))), _ => Err(Report::msg(format!("Unknown command: {}", s))),
} }
} }
@ -73,7 +74,7 @@ fn test_arg_parse() {
HazeArgs::parse(vec!["haze"].into_iter()).unwrap(), HazeArgs::parse(vec!["haze"].into_iter()).unwrap(),
HazeArgs { HazeArgs {
id: None, id: None,
command: Command::List, command: HazeCommand::List,
options: Vec::new(), options: Vec::new(),
} }
); );
@ -81,7 +82,7 @@ fn test_arg_parse() {
HazeArgs::parse(vec!["haze", "test"].into_iter()).unwrap(), HazeArgs::parse(vec!["haze", "test"].into_iter()).unwrap(),
HazeArgs { HazeArgs {
id: None, id: None,
command: Command::Test, command: HazeCommand::Test,
options: Vec::new(), options: Vec::new(),
} }
); );
@ -89,7 +90,7 @@ fn test_arg_parse() {
HazeArgs::parse(vec!["haze", "asdasd"].into_iter()).unwrap(), HazeArgs::parse(vec!["haze", "asdasd"].into_iter()).unwrap(),
HazeArgs { HazeArgs {
id: Some("asdasd".to_string()), id: Some("asdasd".to_string()),
command: Command::List, command: HazeCommand::List,
options: Vec::new(), options: Vec::new(),
} }
); );
@ -97,7 +98,7 @@ fn test_arg_parse() {
HazeArgs::parse(vec!["haze", "asdasd", "db"].into_iter()).unwrap(), HazeArgs::parse(vec!["haze", "asdasd", "db"].into_iter()).unwrap(),
HazeArgs { HazeArgs {
id: Some("asdasd".to_string()), id: Some("asdasd".to_string()),
command: Command::Db, command: HazeCommand::Db,
options: Vec::new(), options: Vec::new(),
} }
); );
@ -105,7 +106,7 @@ fn test_arg_parse() {
HazeArgs::parse(vec!["haze", "exec", "foo", "bar"].into_iter()).unwrap(), HazeArgs::parse(vec!["haze", "exec", "foo", "bar"].into_iter()).unwrap(),
HazeArgs { HazeArgs {
id: None, id: None,
command: Command::Exec, command: HazeCommand::Exec,
options: vec!["foo".to_string(), "bar".to_string()], options: vec!["foo".to_string(), "bar".to_string()],
} }
); );
@ -113,7 +114,7 @@ fn test_arg_parse() {
HazeArgs::parse(vec!["haze", "asdasd", "exec", "foo", "bar"].into_iter()).unwrap(), HazeArgs::parse(vec!["haze", "asdasd", "exec", "foo", "bar"].into_iter()).unwrap(),
HazeArgs { HazeArgs {
id: Some("asdasd".to_string()), id: Some("asdasd".to_string()),
command: Command::Exec, command: HazeCommand::Exec,
options: vec!["foo".to_string(), "bar".to_string()], options: vec!["foo".to_string(), "bar".to_string()],
} }
); );

View file

@ -14,7 +14,7 @@ use std::time::Duration;
use tokio::fs::{create_dir_all, remove_dir_all, write}; use tokio::fs::{create_dir_all, remove_dir_all, write};
use tokio::time::sleep; use tokio::time::sleep;
#[derive(Debug)] #[derive(Debug, Eq, PartialEq)]
#[allow(dead_code)] #[allow(dead_code)]
pub enum Database { pub enum Database {
Sqlite, Sqlite,
@ -43,15 +43,30 @@ impl Default for Database {
} }
impl FromStr for Database { impl FromStr for Database {
type Err = (); type Err = Report;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"sqlite" => Ok(Database::Sqlite), "sqlite" => Ok(Database::Sqlite),
"mysql" => Ok(Database::Mysql), "mysql" => Ok(Database::Mysql),
"mysql:8" => Ok(Database::Mysql80),
"mysql:5" => Ok(Database::Mysql57),
"mysql:5.7" => Ok(Database::Mysql57),
"mysql:5.6" => Ok(Database::Mysql56),
"mariadb" => Ok(Database::MariaDB), "mariadb" => Ok(Database::MariaDB),
"postgresql" => Ok(Database::Postgres), "mariadb:10.1" => Ok(Database::MariaDB101),
_ => Err(()), "mariadb:10.2" => Ok(Database::MariaDB102),
"mariadb:10.3" => Ok(Database::MariaDB103),
"mariadb:10.4" => Ok(Database::MariaDB104),
"mariadb:10.5" => Ok(Database::MariaDB105),
"mariadb:10" => Ok(Database::MariaDB105),
"pgsql" => Ok(Database::Postgres),
"pgsql:9" => Ok(Database::Postgres9),
"pgsql:10" => Ok(Database::Postgres10),
"pgsql:11" => Ok(Database::Postgres11),
"pgsql:12" => Ok(Database::Postgres12),
"pgsql:13" => Ok(Database::Postgres13),
_ => Err(Report::msg("Unknown db type")),
} }
} }
} }
@ -163,7 +178,7 @@ impl Database {
} }
} }
#[derive(Debug)] #[derive(Debug, Eq, PartialEq)]
#[allow(dead_code)] #[allow(dead_code)]
pub enum PhpVersion { pub enum PhpVersion {
Latest, Latest,
@ -172,6 +187,18 @@ pub enum PhpVersion {
// Php73, // Php73,
} }
impl FromStr for PhpVersion {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"7" => Ok(PhpVersion::Php74),
"7.4" => Ok(PhpVersion::Php74),
_ => Err(()),
}
}
}
impl PhpVersion { impl PhpVersion {
fn image(&self) -> &'static str { fn image(&self) -> &'static str {
// for now only 7.4 // for now only 7.4
@ -181,6 +208,13 @@ impl PhpVersion {
} }
} }
pub fn name(&self) -> &'static str {
match self {
PhpVersion::Latest => "7.4",
PhpVersion::Php74 => "7.4",
}
}
async fn spawn( async fn spawn(
&self, &self,
docker: &mut Docker, docker: &mut Docker,
@ -206,6 +240,7 @@ impl PhpVersion {
labels: Some(hashmap! { labels: Some(hashmap! {
"haze-type".to_string() => "cloud".to_string(), "haze-type".to_string() => "cloud".to_string(),
"haze-db".to_string() => db.name().to_string(), "haze-db".to_string() => db.name().to_string(),
"haze-php".to_string() => self.name().to_string(),
"haze-cloud-id".to_string() => id.to_string(), "haze-cloud-id".to_string() => id.to_string(),
}), }),
..Default::default() ..Default::default()
@ -223,20 +258,97 @@ impl Default for PhpVersion {
} }
} }
#[derive(Default, Debug)] #[derive(Default, Debug, Eq, PartialEq)]
pub struct CloudOptions { pub struct CloudOptions {
db: Database, db: Database,
php: PhpVersion, php: PhpVersion,
} }
impl CloudOptions {
pub fn parse<S>(options: Vec<S>) -> Result<(CloudOptions, Vec<S>)>
where
S: AsRef<str> + Clone,
{
let mut db = Database::default();
let mut php = PhpVersion::default();
let mut used = 0;
for option in options.iter() {
if let Ok(db_option) = Database::from_str(option.as_ref()) {
db = db_option;
used += 1;
continue;
}
if let Ok(php_option) = PhpVersion::from_str(option.as_ref()) {
php = php_option;
used += 1;
continue;
}
}
let rest = options[used..].to_vec();
Ok((CloudOptions { db, php }, rest))
}
}
#[test]
fn test_option_parse() {
assert_eq!(
CloudOptions::parse::<&str>(vec![]).unwrap(),
(CloudOptions::default(), vec![])
);
assert_eq!(
CloudOptions::parse(vec!["mariadb"]).unwrap(),
(
CloudOptions {
db: Database::MariaDB,
..Default::default()
},
vec![]
)
);
assert_eq!(
CloudOptions::parse(vec!["rest"]).unwrap(),
(
CloudOptions {
..Default::default()
},
vec!["rest"]
)
);
assert_eq!(
CloudOptions::parse(vec!["7"]).unwrap(),
(
CloudOptions {
php: PhpVersion::Php74,
..Default::default()
},
vec![]
)
);
assert_eq!(
CloudOptions::parse(vec!["7", "pgsql", "rest"]).unwrap(),
(
CloudOptions {
php: PhpVersion::Php74,
db: Database::Postgres,
..Default::default()
},
vec!["rest"]
)
);
}
#[derive(Debug)] #[derive(Debug)]
pub struct Cloud { pub struct Cloud {
pub id: String, pub id: String,
network: String, pub network: String,
containers: Vec<String>, pub containers: Vec<String>,
db: Database, pub php: PhpVersion,
pub db: Database,
pub ip: IpAddr, pub ip: IpAddr,
workdir: Utf8PathBuf, pub workdir: Utf8PathBuf,
} }
impl Cloud { impl Cloud {
@ -360,6 +472,7 @@ impl Cloud {
id, id,
network, network,
containers, containers,
php: options.php,
db: options.db, db: options.db,
ip, ip,
workdir, workdir,
@ -411,7 +524,7 @@ async fn setup_workdir(base: &Utf8Path, id: &str) -> Result<Utf8PathBuf> {
Ok(workdir) Ok(workdir)
} }
pub async fn parse(docker: &mut Docker, config: &HazeConfig) -> Result<Vec<Cloud>> { pub async fn list(docker: &mut Docker, config: &HazeConfig) -> Result<Vec<Cloud>> {
let containers = docker.list_containers::<String>(None).await?; let containers = docker.list_containers::<String>(None).await?;
let mut containers_by_id: HashMap<String, (Option<_>, Vec<_>)> = HashMap::new(); let mut containers_by_id: HashMap<String, (Option<_>, Vec<_>)> = HashMap::new();
for container in containers { for container in containers {
@ -434,7 +547,9 @@ pub async fn parse(docker: &mut Docker, config: &HazeConfig) -> Result<Vec<Cloud
let networks = cloud.network_settings?.networks?; let networks = cloud.network_settings?.networks?;
let network_info = networks.get(&network)?; let network_info = networks.get(&network)?;
let workdir = config.work_dir.join(&id); let workdir = config.work_dir.join(&id);
let db = cloud.labels?.get("haze-db")?.parse().ok()?; let labels = cloud.labels?;
let db = labels.get("haze-db")?.parse().ok()?;
let php = labels.get("haze-php")?.parse().ok()?;
let mut service_ids: Vec<String> = services let mut service_ids: Vec<String> = services
.iter() .iter()
.filter_map(|service| service.names.as_ref()?.first().map(String::clone)) .filter_map(|service| service.names.as_ref()?.first().map(String::clone))
@ -444,6 +559,7 @@ pub async fn parse(docker: &mut Docker, config: &HazeConfig) -> Result<Vec<Cloud
id, id,
network, network,
db, db,
php,
containers: service_ids, containers: service_ids,
ip: network_info.ip_address.as_ref()?.parse().ok()?, ip: network_info.ip_address.as_ref()?.parse().ok()?,
workdir, workdir,

View file

@ -1,7 +1,8 @@
use crate::cloud::{parse, Cloud, CloudOptions}; use crate::args::{HazeArgs, HazeCommand};
use crate::cloud::{list, Cloud, CloudOptions};
use crate::config::HazeConfig; use crate::config::HazeConfig;
use bollard::Docker; use bollard::Docker;
use color_eyre::{eyre::WrapErr, Result}; use color_eyre::{eyre::WrapErr, Report, Result};
mod args; mod args;
mod cloud; mod cloud;
@ -15,13 +16,46 @@ async fn main() -> Result<()> {
sources_root: "/srv/http/owncloud".into(), sources_root: "/srv/http/owncloud".into(),
work_dir: "/tmp/haze".into(), work_dir: "/tmp/haze".into(),
}; };
let options = CloudOptions::default();
// let cloud = Cloud::create(&mut docker, options, &config).await?; let args = HazeArgs::parse(std::env::args())?;
// println!("{} running on http://{}", cloud.id, cloud.ip); dbg!(&args);
let clouds = parse(&mut docker, &config).await?; match args.command {
dbg!(clouds); HazeCommand::Clean => {
let list = list(&mut docker, &config).await?;
for cloud in list {
if let Err(e) = cloud.destroy(&mut docker).await {
eprintln!("Error while removing cloud: {:#}", e);
}
}
}
HazeCommand::List => {
let list = list(&mut docker, &config).await?;
for cloud in list {
if let Some(filter) = &args.id {
if !cloud.id.contains(filter.as_str()) {
continue;
}
}
println!(
"Cloud {}, {}, {}, running on http://{}",
cloud.id,
cloud.php.name(),
cloud.db.name(),
cloud.ip
)
}
}
HazeCommand::Start => {
let (options, rest) = CloudOptions::parse(args.options)?;
if let Some(next) = rest.first() {
return Err(Report::msg(format!("Unknown option {}", next)));
}
let cloud = Cloud::create(&mut docker, options, &config).await?;
println!("http://{}", cloud.ip);
}
_ => todo!(),
};
Ok(()) Ok(())
} }