mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
stability fixes
This commit is contained in:
committed by
Aiden McClelland
parent
7696ec9a13
commit
071f6cec03
2
Makefile
2
Makefile
@@ -11,6 +11,8 @@ PATCH_DB_CLIENT_SRC = $(shell find patch-db/client -not -path patch-db/client/di
|
||||
GIT_REFS := $(shell find .git/refs/heads)
|
||||
TMP_FILE := $(shell mktemp)
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
all: eos.img
|
||||
|
||||
gzip: eos.img
|
||||
|
||||
@@ -3,7 +3,9 @@ use std::collections::BTreeMap;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use bollard::container::StopContainerOptions;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tracing::instrument;
|
||||
@@ -46,17 +48,39 @@ impl DockerAction {
|
||||
volumes: &Volumes,
|
||||
input: Option<I>,
|
||||
allow_inject: bool,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
let mut cmd = tokio::process::Command::new("docker");
|
||||
let mut timeout_fut =
|
||||
futures::future::Either::Left(futures::future::pending::<Result<_, Error>>());
|
||||
if self.inject && allow_inject {
|
||||
cmd.arg("exec");
|
||||
} else {
|
||||
let container_name = Self::container_name(pkg_id, name);
|
||||
cmd.arg("run")
|
||||
.arg("--rm")
|
||||
.arg("--network=start9")
|
||||
.arg(format!("--add-host=embassy:{}", Ipv4Addr::from(HOST_IP)))
|
||||
.arg("--name")
|
||||
.arg(Self::container_name(pkg_id, name));
|
||||
.arg(&container_name);
|
||||
if let Some(timeout) = timeout {
|
||||
timeout_fut = futures::future::Either::Right(async move {
|
||||
tokio::time::sleep(timeout).await;
|
||||
|
||||
match ctx
|
||||
.docker
|
||||
.stop_container(&container_name, Some(StopContainerOptions { t: 30 }))
|
||||
.await
|
||||
{
|
||||
Err(bollard::errors::Error::DockerResponseNotFoundError { .. })
|
||||
| Err(bollard::errors::Error::DockerResponseConflictError { .. })
|
||||
| Err(bollard::errors::Error::DockerResponseNotModifiedError { .. }) => (), // Already stopped
|
||||
a => a?,
|
||||
};
|
||||
|
||||
Ok(futures::future::pending().await)
|
||||
});
|
||||
}
|
||||
}
|
||||
cmd.args(
|
||||
self.docker_args(ctx, pkg_id, pkg_version, volumes, allow_inject)
|
||||
@@ -85,10 +109,13 @@ impl DockerAction {
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Docker)?;
|
||||
}
|
||||
let res = handle
|
||||
let res = tokio::select! {
|
||||
res = handle
|
||||
.wait_with_output()
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Docker)?;
|
||||
=> res
|
||||
.with_kind(crate::ErrorKind::Docker)?,
|
||||
res = timeout_fut => res?,
|
||||
};
|
||||
Ok(if res.status.success() || res.status.code() == Some(143) {
|
||||
Ok(if let Some(format) = &self.io_format {
|
||||
match format.from_slice(&res.stdout) {
|
||||
@@ -125,6 +152,7 @@ impl DockerAction {
|
||||
pkg_version: &Version,
|
||||
volumes: &Volumes,
|
||||
input: Option<I>,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
let mut cmd = tokio::process::Command::new("docker");
|
||||
cmd.arg("run").arg("--rm").arg("--network=none");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use color_eyre::eyre::eyre;
|
||||
@@ -131,6 +132,7 @@ impl Action {
|
||||
volumes,
|
||||
input,
|
||||
true,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.map_err(|e| Error::new(eyre!("{}", e.1), crate::ErrorKind::Action))
|
||||
@@ -154,11 +156,21 @@ impl ActionImplementation {
|
||||
volumes: &Volumes,
|
||||
input: Option<I>,
|
||||
allow_inject: bool,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
match self {
|
||||
ActionImplementation::Docker(action) => {
|
||||
action
|
||||
.execute(ctx, pkg_id, pkg_version, name, volumes, input, allow_inject)
|
||||
.execute(
|
||||
ctx,
|
||||
pkg_id,
|
||||
pkg_version,
|
||||
name,
|
||||
volumes,
|
||||
input,
|
||||
allow_inject,
|
||||
timeout,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -171,11 +183,12 @@ impl ActionImplementation {
|
||||
pkg_version: &Version,
|
||||
volumes: &Volumes,
|
||||
input: Option<I>,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
match self {
|
||||
ActionImplementation::Docker(action) => {
|
||||
action
|
||||
.sandboxed(ctx, pkg_id, pkg_version, volumes, input)
|
||||
.sandboxed(ctx, pkg_id, pkg_version, volumes, input, timeout)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ impl BackupActions {
|
||||
&volumes,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.map_err(|e| eyre!("{}", e.1))
|
||||
@@ -167,6 +168,7 @@ impl BackupActions {
|
||||
&volumes,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.map_err(|e| eyre!("{}", e.1))
|
||||
|
||||
@@ -46,6 +46,7 @@ impl ConfigActions {
|
||||
volumes,
|
||||
None::<()>,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.and_then(|res| {
|
||||
@@ -73,6 +74,7 @@ impl ConfigActions {
|
||||
volumes,
|
||||
Some(input),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.and_then(|res| {
|
||||
|
||||
@@ -23,9 +23,7 @@ use crate::dependencies::{
|
||||
};
|
||||
use crate::install::cleanup::remove_current_dependents;
|
||||
use crate::s9pk::manifest::{Manifest, PackageId};
|
||||
use crate::util::{
|
||||
display_none, display_serializable, parse_duration, parse_stdin_deserializable, IoFormat,
|
||||
};
|
||||
use crate::util::{display_none, display_serializable, parse_stdin_deserializable, IoFormat};
|
||||
use crate::{Error, ResultExt as _};
|
||||
|
||||
pub mod action;
|
||||
@@ -190,11 +188,11 @@ pub fn set(
|
||||
#[allow(unused_variables)]
|
||||
#[arg(long = "format")]
|
||||
format: Option<IoFormat>,
|
||||
#[arg(long = "timeout", parse(parse_duration))] timeout: Option<Duration>,
|
||||
#[arg(long = "timeout")] timeout: Option<crate::util::Duration>,
|
||||
#[arg(stdin, parse(parse_stdin_deserializable))] config: Option<Config>,
|
||||
#[arg(rename = "expire-id", long = "expire-id")] expire_id: Option<String>,
|
||||
) -> Result<(PackageId, Option<Config>, Option<Duration>, Option<String>), Error> {
|
||||
Ok((id, config, timeout, expire_id))
|
||||
Ok((id, config, timeout.map(|d| *d), expire_id))
|
||||
}
|
||||
|
||||
#[command(rename = "dry", display(display_serializable))]
|
||||
|
||||
@@ -421,6 +421,7 @@ impl DependencyConfig {
|
||||
dependent_version,
|
||||
dependent_volumes,
|
||||
Some(dependency_config),
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.map_err(|(_, e)| e))
|
||||
@@ -440,6 +441,7 @@ impl DependencyConfig {
|
||||
dependent_version,
|
||||
dependent_volumes,
|
||||
Some(old),
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.map_err(|e| Error::new(eyre!("{}", e.1), crate::ErrorKind::AutoConfigure))
|
||||
@@ -472,7 +474,7 @@ pub async fn configure_impl(
|
||||
&mut db,
|
||||
&dep_id,
|
||||
Some(new_config),
|
||||
&Some(Duration::from_secs(3)),
|
||||
&Some(Duration::from_secs(3).into()),
|
||||
false,
|
||||
&mut BTreeMap::new(),
|
||||
&mut BTreeMap::new(),
|
||||
@@ -598,7 +600,14 @@ pub async fn configure_logic(
|
||||
|
||||
let new_config = dependency
|
||||
.auto_configure
|
||||
.sandboxed(&ctx, &pkg_id, &pkg_version, &pkg_volumes, Some(&old_config))
|
||||
.sandboxed(
|
||||
&ctx,
|
||||
&pkg_id,
|
||||
&pkg_version,
|
||||
&pkg_volumes,
|
||||
Some(&old_config),
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.map_err(|e| Error::new(eyre!("{}", e.1), crate::ErrorKind::AutoConfigure))?;
|
||||
|
||||
|
||||
@@ -83,7 +83,9 @@ pub async fn get_vendor<P: AsRef<Path>>(path: P) -> Result<Option<String>, Error
|
||||
.join("device")
|
||||
.join("vendor"),
|
||||
)
|
||||
.await?;
|
||||
.await?
|
||||
.trim()
|
||||
.to_owned();
|
||||
Ok(if vendor.is_empty() {
|
||||
None
|
||||
} else {
|
||||
@@ -104,7 +106,9 @@ pub async fn get_model<P: AsRef<Path>>(path: P) -> Result<Option<String>, Error>
|
||||
.join("device")
|
||||
.join("model"),
|
||||
)
|
||||
.await?;
|
||||
.await?
|
||||
.trim()
|
||||
.to_owned();
|
||||
Ok(if model.is_empty() { None } else { Some(model) })
|
||||
}
|
||||
|
||||
|
||||
@@ -183,6 +183,7 @@ async fn run_main(
|
||||
&rt_state.manifest.volumes,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
});
|
||||
|
||||
@@ -44,6 +44,7 @@ impl Migrations {
|
||||
volumes,
|
||||
Some(version),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.map_err(|e| {
|
||||
@@ -78,6 +79,7 @@ impl Migrations {
|
||||
volumes,
|
||||
Some(version),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.map_err(|e| {
|
||||
|
||||
@@ -92,6 +92,13 @@ impl NginxControllerInner {
|
||||
if lan_port_config.ssl {
|
||||
// these have already been written by the net controller
|
||||
let package_path = nginx_root.join(format!("ssl/{}", package));
|
||||
if tokio::fs::metadata(&package_path).await.is_err() {
|
||||
tokio::fs::create_dir_all(&package_path)
|
||||
.await
|
||||
.with_ctx(|_| {
|
||||
(ErrorKind::Filesystem, package_path.display().to_string())
|
||||
})?;
|
||||
}
|
||||
let ssl_path_key = package_path.join(format!("{}.key.pem", id));
|
||||
let ssl_path_cert = package_path.join(format!("{}.cert.pem", id));
|
||||
let (key, chain) = ssl_manager.certificate_for(&meta.dns_base).await?;
|
||||
|
||||
@@ -39,6 +39,7 @@ pub async fn fetch_properties(ctx: RpcContext, id: PackageId) -> Result<Value, E
|
||||
&manifest.volumes,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.map_err(|(_, e)| Error::new(eyre!("{}", e), ErrorKind::Docker))
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::action::{ActionImplementation, NoOutput};
|
||||
use crate::context::RpcContext;
|
||||
use crate::id::Id;
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::util::Version;
|
||||
use crate::util::{Duration, Version};
|
||||
use crate::volume::Volumes;
|
||||
use crate::Error;
|
||||
|
||||
@@ -72,6 +72,7 @@ pub struct HealthCheck {
|
||||
#[serde(flatten)]
|
||||
implementation: ActionImplementation,
|
||||
pub critical: bool,
|
||||
pub timeout: Option<Duration>,
|
||||
}
|
||||
impl HealthCheck {
|
||||
#[instrument(skip(ctx))]
|
||||
@@ -94,6 +95,10 @@ impl HealthCheck {
|
||||
volumes,
|
||||
Some(Utc::now().signed_duration_since(started).num_milliseconds()),
|
||||
true,
|
||||
Some(
|
||||
self.timeout
|
||||
.map_or(std::time::Duration::from_secs(30), |d| *d),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
Ok(match res {
|
||||
|
||||
@@ -7,7 +7,6 @@ use std::path::{Path, PathBuf};
|
||||
use std::process::{exit, Stdio};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use clap::ArgMatches;
|
||||
@@ -651,30 +650,98 @@ pub fn parse_stdin_deserializable<T: for<'de> Deserialize<'de>>(
|
||||
format.from_reader(stdin)
|
||||
}
|
||||
|
||||
pub fn parse_duration(arg: &str, _: &ArgMatches<'_>) -> Result<Duration, Error> {
|
||||
let units_idx = arg.find(|c: char| c.is_alphabetic()).ok_or_else(|| {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Duration(std::time::Duration);
|
||||
impl Deref for Duration {
|
||||
type Target = std::time::Duration;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl From<std::time::Duration> for Duration {
|
||||
fn from(t: std::time::Duration) -> Self {
|
||||
Duration(t)
|
||||
}
|
||||
}
|
||||
impl std::str::FromStr for Duration {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let units_idx = s.find(|c: char| c.is_alphabetic()).ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("Must specify units for duration"),
|
||||
crate::ErrorKind::Deserialization,
|
||||
)
|
||||
})?;
|
||||
let (num, units) = arg.split_at(units_idx);
|
||||
match units {
|
||||
"d" if num.contains(".") => Ok(Duration::from_secs_f64(num.parse::<f64>()? * 86400_f64)),
|
||||
"d" => Ok(Duration::from_secs(num.parse::<u64>()? * 86400)),
|
||||
"h" if num.contains(".") => Ok(Duration::from_secs_f64(num.parse::<f64>()? * 3600_f64)),
|
||||
"h" => Ok(Duration::from_secs(num.parse::<u64>()? * 3600)),
|
||||
"m" if num.contains(".") => Ok(Duration::from_secs_f64(num.parse::<f64>()? * 60_f64)),
|
||||
"m" => Ok(Duration::from_secs(num.parse::<u64>()? * 60)),
|
||||
"s" if num.contains(".") => Ok(Duration::from_secs_f64(num.parse()?)),
|
||||
"s" => Ok(Duration::from_secs(num.parse()?)),
|
||||
"ms" => Ok(Duration::from_millis(num.parse()?)),
|
||||
"us" => Ok(Duration::from_micros(num.parse()?)),
|
||||
"ns" => Ok(Duration::from_nanos(num.parse()?)),
|
||||
_ => Err(Error::new(
|
||||
let (num, units) = s.split_at(units_idx);
|
||||
use std::time::Duration;
|
||||
Ok(Duration(match units {
|
||||
"d" if num.contains(".") => Duration::from_secs_f64(num.parse::<f64>()? * 86_400_f64),
|
||||
"d" => Duration::from_secs(num.parse::<u64>()? * 86_400),
|
||||
"h" if num.contains(".") => Duration::from_secs_f64(num.parse::<f64>()? * 3_600_f64),
|
||||
"h" => Duration::from_secs(num.parse::<u64>()? * 3_600),
|
||||
"m" if num.contains(".") => Duration::from_secs_f64(num.parse::<f64>()? * 60_f64),
|
||||
"m" => Duration::from_secs(num.parse::<u64>()? * 60),
|
||||
"s" if num.contains(".") => Duration::from_secs_f64(num.parse()?),
|
||||
"s" => Duration::from_secs(num.parse()?),
|
||||
"ms" if num.contains(".") => Duration::from_secs_f64(num.parse::<f64>()? / 1_000_f64),
|
||||
"ms" => {
|
||||
let millis: u128 = num.parse()?;
|
||||
Duration::new((millis / 1_000) as u64, (millis % 1_000) as u32)
|
||||
}
|
||||
"us" | "µs" if num.contains(".") => {
|
||||
Duration::from_secs_f64(num.parse::<f64>()? / 1_000_000_f64)
|
||||
}
|
||||
"us" | "µs" => {
|
||||
let micros: u128 = num.parse()?;
|
||||
Duration::new((micros / 1_000_000) as u64, (micros % 1_000_000) as u32)
|
||||
}
|
||||
"ns" if num.contains(".") => {
|
||||
Duration::from_secs_f64(num.parse::<f64>()? / 1_000_000_000_f64)
|
||||
}
|
||||
"ns" => {
|
||||
let nanos: u128 = num.parse()?;
|
||||
Duration::new(
|
||||
(nanos / 1_000_000_000) as u64,
|
||||
(nanos % 1_000_000_000) as u32,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
eyre!("Invalid units for duration"),
|
||||
crate::ErrorKind::Deserialization,
|
||||
)),
|
||||
))
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for Duration {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let nanos = self.as_nanos();
|
||||
match () {
|
||||
_ if nanos % 86_400_000_000_000 == 0 => write!(f, "{}d", nanos / 86_400_000_000_000),
|
||||
_ if nanos % 3_600_000_000_000 == 0 => write!(f, "{}h", nanos / 3_600_000_000_000),
|
||||
_ if nanos % 60_000_000_000 == 0 => write!(f, "{}m", nanos / 60_000_000_000),
|
||||
_ if nanos % 1_000_000_000 == 0 => write!(f, "{}s", nanos / 1_000_000_000),
|
||||
_ if nanos % 1_000_000 == 0 => write!(f, "{}ms", nanos / 1_000_000),
|
||||
_ if nanos % 1_000 == 0 => write!(f, "{}µs", nanos / 1_000),
|
||||
_ => write!(f, "{}ns", nanos),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'de> Deserialize<'de> for Duration {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_from_str(deserializer)
|
||||
}
|
||||
}
|
||||
impl Serialize for Duration {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serialize_display(self, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user