mirror of
https://codeberg.org/icewind/haze.git
synced 2026-06-03 17:14:08 +02:00
waiting
This commit is contained in:
parent
c2f85e009e
commit
1d20b7937c
5 changed files with 154 additions and 29 deletions
42
src/cloud.rs
42
src/cloud.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::config::HazeConfig;
|
use crate::config::HazeConfig;
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
|
use crate::exec::{exec, exec_tty};
|
||||||
use crate::php::PhpVersion;
|
use crate::php::PhpVersion;
|
||||||
use crate::tty::exec_tty;
|
|
||||||
use bollard::container::{ListContainersOptions, LogsOptions, RemoveContainerOptions};
|
use bollard::container::{ListContainersOptions, LogsOptions, RemoveContainerOptions};
|
||||||
use bollard::models::ContainerState;
|
use bollard::models::ContainerState;
|
||||||
use bollard::network::CreateNetworkOptions;
|
use bollard::network::CreateNetworkOptions;
|
||||||
|
|
@ -10,17 +10,17 @@ use camino::{Utf8Path, Utf8PathBuf};
|
||||||
use color_eyre::{eyre::WrapErr, Report, Result};
|
use color_eyre::{eyre::WrapErr, Report, Result};
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
use petname::petname;
|
use petname::petname;
|
||||||
use reqwest::{Client, Url};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::io::stdout;
|
||||||
use std::iter::Peekable;
|
use std::iter::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;
|
||||||
use std::time::Duration;
|
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, timeout};
|
use tokio::time::sleep;
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, Eq, PartialEq)]
|
#[derive(Clone, Default, Debug, Eq, PartialEq)]
|
||||||
pub struct CloudOptions {
|
pub struct CloudOptions {
|
||||||
|
|
@ -288,8 +288,17 @@ impl Cloud {
|
||||||
Ok(logs)
|
Ok(logs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn exec<S: Into<String>>(&self, docker: &mut Docker, cmd: Vec<S>) -> Result<()> {
|
pub async fn exec<S: Into<String>>(
|
||||||
|
&self,
|
||||||
|
docker: &mut Docker,
|
||||||
|
cmd: Vec<S>,
|
||||||
|
tty: bool,
|
||||||
|
) -> Result<i64> {
|
||||||
|
if tty {
|
||||||
exec_tty(docker, &self.id, "haze", cmd, vec![]).await
|
exec_tty(docker, &self.id, "haze", cmd, vec![]).await
|
||||||
|
} else {
|
||||||
|
exec(docker, &self.id, "haze", cmd, vec![], Some(stdout())).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list(
|
pub async fn list(
|
||||||
|
|
@ -372,19 +381,16 @@ impl Cloud {
|
||||||
.ok_or(Report::msg("No clouds running matching filter"))
|
.ok_or(Report::msg("No clouds running matching filter"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn wait_for_start(&self) -> Result<()> {
|
pub async fn wait_for_start(&self, docker: &mut Docker) -> Result<()> {
|
||||||
let client = Client::new();
|
self.php
|
||||||
let url = Url::parse(&format!(
|
.wait_for_start(self.ip)
|
||||||
"http://{}/status.php",
|
|
||||||
self.ip.ok_or(Report::msg("Container not running"))?
|
|
||||||
))?;
|
|
||||||
timeout(Duration::from_secs(5), async {
|
|
||||||
while !client.get(url.clone()).send().await.is_ok() {
|
|
||||||
sleep(Duration::from_millis(100)).await
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
.await
|
||||||
.wrap_err("Timeout after 5 seconds")
|
.wrap_err("Failed to wait for php container")?;
|
||||||
|
self.db
|
||||||
|
.wait_for_start(docker, &self.id)
|
||||||
|
.await
|
||||||
|
.wrap_err("Failed to wait for database container")?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn enable_app<S: Into<String>>(&self, docker: &mut Docker, app: S) -> Result<()> {
|
pub async fn enable_app<S: Into<String>>(&self, docker: &mut Docker, app: S) -> Result<()> {
|
||||||
|
|
@ -396,8 +402,10 @@ impl Cloud {
|
||||||
app.into(),
|
app.into(),
|
||||||
"--force".to_string(),
|
"--force".to_string(),
|
||||||
],
|
],
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
|
use crate::exec::{exec, exec_tty};
|
||||||
use crate::image::pull_image;
|
use crate::image::pull_image;
|
||||||
use crate::tty::exec_tty;
|
|
||||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||||
use bollard::models::{EndpointSettings, HostConfig};
|
use bollard::models::{EndpointSettings, HostConfig};
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use color_eyre::{Report, Result};
|
use color_eyre::{eyre::WrapErr, Report, Result};
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
|
use std::io::Stdout;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::time::{sleep, timeout};
|
||||||
|
|
||||||
pub enum DatabaseFamily {
|
pub enum DatabaseFamily {
|
||||||
Sqlite,
|
Sqlite,
|
||||||
|
|
@ -198,7 +201,7 @@ impl Database {
|
||||||
Ok(Some(id))
|
Ok(Some(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn exec(&self, docker: &mut Docker, cloud_id: &str) -> Result<()> {
|
pub async fn exec(&self, docker: &mut Docker, cloud_id: &str) -> Result<i64> {
|
||||||
match self.family() {
|
match self.family() {
|
||||||
DatabaseFamily::Sqlite => {
|
DatabaseFamily::Sqlite => {
|
||||||
exec_tty(
|
exec_tty(
|
||||||
|
|
@ -232,4 +235,48 @@ impl Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn wait_for_start(&self, docker: &mut Docker, cloud_id: &str) -> Result<()> {
|
||||||
|
timeout(Duration::from_secs(15), async {
|
||||||
|
while !self.is_healthy(docker, cloud_id).await? {
|
||||||
|
sleep(Duration::from_millis(100)).await
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.wrap_err("Timeout after 15 seconds")?
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_healthy(&self, docker: &mut Docker, cloud_id: &str) -> Result<bool> {
|
||||||
|
match self.family() {
|
||||||
|
DatabaseFamily::Sqlite => Ok(true),
|
||||||
|
DatabaseFamily::Mysql => Ok(true),
|
||||||
|
DatabaseFamily::MariaDB => Ok(true),
|
||||||
|
DatabaseFamily::Postgres => {
|
||||||
|
let is_ready_status = exec(
|
||||||
|
docker,
|
||||||
|
format!("{}-db", cloud_id),
|
||||||
|
"root",
|
||||||
|
vec!["pg_isready", "-U", "haze", "-q"],
|
||||||
|
vec![],
|
||||||
|
Option::<Stdout>::None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
if is_ready_status == 0 {
|
||||||
|
let connect_status = exec(
|
||||||
|
docker,
|
||||||
|
format!("{}-db", cloud_id),
|
||||||
|
"root",
|
||||||
|
vec!["psql", "-U", "haze", "-qtA", "-c", ""],
|
||||||
|
vec![],
|
||||||
|
Option::<Stdout>::None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(connect_status == 0)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use bollard::exec::{CreateExecOptions, StartExecResults};
|
use bollard::exec::{CreateExecOptions, StartExecResults};
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use color_eyre::{eyre::WrapErr, Result};
|
use color_eyre::{eyre::WrapErr, Result};
|
||||||
|
use futures_util::StreamExt;
|
||||||
use std::io::{stdout, Read, Write};
|
use std::io::{stdout, Read, Write};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use termion::async_stdin;
|
use termion::async_stdin;
|
||||||
|
|
@ -15,7 +16,7 @@ pub async fn exec_tty<S1: AsRef<str>, S2: Into<String>>(
|
||||||
user: &str,
|
user: &str,
|
||||||
cmd: Vec<S2>,
|
cmd: Vec<S2>,
|
||||||
env: Vec<&str>,
|
env: Vec<&str>,
|
||||||
) -> Result<()> {
|
) -> Result<i64> {
|
||||||
let cmd = cmd.into_iter().map(S2::into).collect();
|
let cmd = cmd.into_iter().map(S2::into).collect();
|
||||||
let env = env.into_iter().map(String::from).collect();
|
let env = env.into_iter().map(String::from).collect();
|
||||||
let config = CreateExecOptions {
|
let config = CreateExecOptions {
|
||||||
|
|
@ -68,5 +69,53 @@ pub async fn exec_tty<S1: AsRef<str>, S2: Into<String>>(
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
|
Ok(docker
|
||||||
|
.inspect_exec(&message.id)
|
||||||
|
.await?
|
||||||
|
.exit_code
|
||||||
|
.unwrap_or_default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn exec<S1: AsRef<str>, S2: Into<String>>(
|
||||||
|
docker: &mut Docker,
|
||||||
|
container: S1,
|
||||||
|
user: &str,
|
||||||
|
cmd: Vec<S2>,
|
||||||
|
env: Vec<&str>,
|
||||||
|
mut std_out: Option<impl Write>,
|
||||||
|
) -> Result<i64> {
|
||||||
|
let cmd = cmd.into_iter().map(S2::into).collect();
|
||||||
|
let env = env.into_iter().map(String::from).collect();
|
||||||
|
let config = CreateExecOptions {
|
||||||
|
cmd: Some(cmd),
|
||||||
|
user: Some(user.to_string()),
|
||||||
|
attach_stdout: Some(true),
|
||||||
|
attach_stderr: Some(true),
|
||||||
|
env: Some(env),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let message = docker
|
||||||
|
.create_exec(container.as_ref(), config)
|
||||||
|
.await
|
||||||
|
.wrap_err("Failed to setup exec")?;
|
||||||
|
if let StartExecResults::Attached { mut output, .. } = docker
|
||||||
|
.start_exec(&message.id, None, false)
|
||||||
|
.await
|
||||||
|
.wrap_err("Failed to start exec")?
|
||||||
|
{
|
||||||
|
while let Some(Ok(line)) = output.next().await {
|
||||||
|
if let Some(std_out) = &mut std_out {
|
||||||
|
write!(std_out, "{}", line)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(docker
|
||||||
|
.inspect_exec(&message.id)
|
||||||
|
.await?
|
||||||
|
.exit_code
|
||||||
|
.unwrap_or_default())
|
||||||
}
|
}
|
||||||
10
src/main.rs
10
src/main.rs
|
|
@ -8,9 +8,9 @@ mod args;
|
||||||
mod cloud;
|
mod cloud;
|
||||||
mod config;
|
mod config;
|
||||||
mod database;
|
mod database;
|
||||||
|
mod exec;
|
||||||
mod image;
|
mod image;
|
||||||
mod php;
|
mod php;
|
||||||
mod tty;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
|
@ -74,6 +74,7 @@ async fn main() -> Result<()> {
|
||||||
} else {
|
} else {
|
||||||
command
|
command
|
||||||
},
|
},
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +84,7 @@ async fn main() -> Result<()> {
|
||||||
} => {
|
} => {
|
||||||
let cloud = Cloud::get_by_filter(&mut docker, filter, &config).await?;
|
let cloud = Cloud::get_by_filter(&mut docker, filter, &config).await?;
|
||||||
command.insert(0, "occ".to_string());
|
command.insert(0, "occ".to_string());
|
||||||
cloud.exec(&mut docker, command).await?;
|
cloud.exec(&mut docker, command, true).await?;
|
||||||
}
|
}
|
||||||
HazeArgs::Db { filter } => {
|
HazeArgs::Db { filter } => {
|
||||||
let cloud = Cloud::get_by_filter(&mut docker, filter, &config).await?;
|
let cloud = Cloud::get_by_filter(&mut docker, filter, &config).await?;
|
||||||
|
|
@ -98,10 +99,10 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
HazeArgs::Test { options, path } => {
|
HazeArgs::Test { options, path } => {
|
||||||
let cloud = Cloud::create(&mut docker, options, &config).await?;
|
let cloud = Cloud::create(&mut docker, options, &config).await?;
|
||||||
cloud.wait_for_start().await?;
|
cloud.wait_for_start(&mut docker).await?;
|
||||||
println!("Installing");
|
println!("Installing");
|
||||||
cloud
|
cloud
|
||||||
.exec(&mut docker, vec!["install", "admin", "admin"])
|
.exec(&mut docker, vec!["install", "admin", "admin"], false)
|
||||||
.await?;
|
.await?;
|
||||||
if let Some(app) = path
|
if let Some(app) = path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -118,6 +119,7 @@ async fn main() -> Result<()> {
|
||||||
.exec(
|
.exec(
|
||||||
&mut docker,
|
&mut docker,
|
||||||
vec!["tests".to_string(), path.unwrap_or_default()],
|
vec!["tests".to_string(), path.unwrap_or_default()],
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
cloud.destroy(&mut docker).await?;
|
cloud.destroy(&mut docker).await?;
|
||||||
|
|
|
||||||
21
src/php.rs
21
src/php.rs
|
|
@ -3,9 +3,13 @@ use crate::image::pull_image;
|
||||||
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
use bollard::container::{Config, CreateContainerOptions, NetworkingConfig};
|
||||||
use bollard::models::{EndpointSettings, HostConfig};
|
use bollard::models::{EndpointSettings, HostConfig};
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use color_eyre::Result;
|
use color_eyre::{eyre::WrapErr, Report, Result};
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
|
use reqwest::{Client, Url};
|
||||||
|
use std::net::IpAddr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::time::{sleep, timeout};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
@ -87,6 +91,21 @@ impl PhpVersion {
|
||||||
docker.start_container::<String>(&id, None).await?;
|
docker.start_container::<String>(&id, None).await?;
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn wait_for_start(&self, ip: Option<IpAddr>) -> Result<()> {
|
||||||
|
let client = Client::new();
|
||||||
|
let url = Url::parse(&format!(
|
||||||
|
"http://{}/status.php",
|
||||||
|
ip.ok_or(Report::msg("Container not running"))?
|
||||||
|
))?;
|
||||||
|
timeout(Duration::from_secs(5), async {
|
||||||
|
while !client.get(url.clone()).send().await.is_ok() {
|
||||||
|
sleep(Duration::from_millis(100)).await
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.wrap_err("Timeout after 5 seconds")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PhpVersion {
|
impl Default for PhpVersion {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue