mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
Feat/long running sockets (#2090)
* wip: Working on sockets, but can't connect? * simplify unix socket connection * wip: Get responses back from the server at least once. * WIP: Get the sockets working' * feat: Sockets can start/ stop/ config/ properites/ uninstall * fix: Restart services * Fix: Sockets work and can stop main and not kill client Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
@@ -2,7 +2,7 @@ use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use async_stream::stream;
|
||||
@@ -11,13 +11,16 @@ use color_eyre::eyre::eyre;
|
||||
use color_eyre::Report;
|
||||
use futures::future::Either as EitherFuture;
|
||||
use futures::TryStreamExt;
|
||||
use helpers::{NonDetachingJoinHandle, RpcClient};
|
||||
use helpers::{NonDetachingJoinHandle, UnixRpcClient};
|
||||
use nix::sys::signal;
|
||||
use nix::unistd::Pid;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tokio::io::{AsyncBufRead, AsyncBufReadExt, BufReader};
|
||||
use tokio::{
|
||||
io::{AsyncBufRead, AsyncBufReadExt, BufReader},
|
||||
time::timeout,
|
||||
};
|
||||
use tracing::instrument;
|
||||
|
||||
use super::ProcedureName;
|
||||
@@ -66,6 +69,7 @@ pub struct DockerContainer {
|
||||
#[serde(default)]
|
||||
pub system: bool,
|
||||
}
|
||||
|
||||
impl DockerContainer {
|
||||
/// We created a new exec runner, where we are going to be passing the commands for it to run.
|
||||
/// Idea is that we are going to send it command and get the inputs be filtered back from the manager.
|
||||
@@ -78,9 +82,16 @@ impl DockerContainer {
|
||||
pkg_id: &PackageId,
|
||||
pkg_version: &Version,
|
||||
volumes: &Volumes,
|
||||
) -> Result<(LongRunning, RpcClient), Error> {
|
||||
) -> Result<(LongRunning, UnixRpcClient), Error> {
|
||||
let container_name = DockerProcedure::container_name(pkg_id, None);
|
||||
|
||||
let socket_path =
|
||||
Path::new("/tmp/embassy/containers").join(format!("{pkg_id}_{pkg_version}"));
|
||||
if tokio::fs::metadata(&socket_path).await.is_ok() {
|
||||
tokio::fs::remove_dir_all(&socket_path).await?;
|
||||
}
|
||||
tokio::fs::create_dir_all(&socket_path).await?;
|
||||
|
||||
let mut cmd = LongRunning::setup_long_running_docker_cmd(
|
||||
self,
|
||||
ctx,
|
||||
@@ -88,20 +99,13 @@ impl DockerContainer {
|
||||
volumes,
|
||||
pkg_id,
|
||||
pkg_version,
|
||||
&socket_path,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut handle = cmd.spawn().with_kind(crate::ErrorKind::Docker)?;
|
||||
|
||||
let client =
|
||||
if let (Some(stdin), Some(stdout)) = (handle.stdin.take(), handle.stdout.take()) {
|
||||
RpcClient::new(stdin, stdout)
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
eyre!("No stdin/stdout handle for container init"),
|
||||
crate::ErrorKind::Incoherent,
|
||||
));
|
||||
};
|
||||
let client = UnixRpcClient::new(socket_path.join("rpc.sock"));
|
||||
|
||||
let running_output = NonDetachingJoinHandle::from(tokio::spawn(async move {
|
||||
if let Err(err) = handle
|
||||
@@ -114,6 +118,19 @@ impl DockerContainer {
|
||||
}
|
||||
}));
|
||||
|
||||
{
|
||||
let socket = socket_path.join("rpc.sock");
|
||||
if let Err(_err) = timeout(Duration::from_secs(1), async move {
|
||||
while tokio::fs::metadata(&socket).await.is_err() {
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
{
|
||||
tracing::error!("Timed out waiting for init to create socket");
|
||||
}
|
||||
}
|
||||
|
||||
Ok((LongRunning { running_output }, client))
|
||||
}
|
||||
}
|
||||
@@ -771,9 +788,10 @@ impl LongRunning {
|
||||
volumes: &Volumes,
|
||||
pkg_id: &PackageId,
|
||||
pkg_version: &Version,
|
||||
socket_path: &Path,
|
||||
) -> Result<tokio::process::Command, Error> {
|
||||
const INIT_EXEC: &str = "/start9/embassy_container_init";
|
||||
const BIND_LOCATION: &str = "/usr/lib/embassy/container";
|
||||
const INIT_EXEC: &str = "/start9/bin/embassy_container_init";
|
||||
const BIND_LOCATION: &str = "/usr/lib/embassy/container/";
|
||||
tracing::trace!("setup_long_running_docker_cmd");
|
||||
|
||||
LongRunning::cleanup_previous_container(ctx, container_name).await?;
|
||||
@@ -799,7 +817,14 @@ impl LongRunning {
|
||||
.arg("--network=start9")
|
||||
.arg(format!("--add-host=embassy:{}", Ipv4Addr::from(HOST_IP)))
|
||||
.arg("--mount")
|
||||
.arg(format!("type=bind,src={BIND_LOCATION},dst=/start9"))
|
||||
.arg(format!(
|
||||
"type=bind,src={BIND_LOCATION},dst=/start9/bin/,readonly"
|
||||
))
|
||||
.arg("--mount")
|
||||
.arg(format!(
|
||||
"type=bind,src={input},dst=/start9/sockets/",
|
||||
input = socket_path.display()
|
||||
))
|
||||
.arg("--name")
|
||||
.arg(&container_name)
|
||||
.arg(format!("--hostname={}", &container_name))
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::time::Duration;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use embassy_container_init::{ProcessGroupId, SignalGroup, SignalGroupParams};
|
||||
use helpers::RpcClient;
|
||||
use helpers::UnixRpcClient;
|
||||
pub use js_engine::JsError;
|
||||
use js_engine::{JsExecutionEnvironment, PathForVolumeId};
|
||||
use models::{ErrorKind, VolumeId};
|
||||
@@ -68,7 +68,7 @@ impl JsProcedure {
|
||||
input: Option<I>,
|
||||
timeout: Option<Duration>,
|
||||
gid: ProcessGroupId,
|
||||
rpc_client: Option<Arc<RpcClient>>,
|
||||
rpc_client: Option<Arc<UnixRpcClient>>,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
let cleaner_client = rpc_client.clone();
|
||||
let cleaner = GeneralGuard::new(move || {
|
||||
@@ -96,7 +96,7 @@ impl JsProcedure {
|
||||
)
|
||||
.await?
|
||||
.run_action(name, input, self.args.clone());
|
||||
let output: ErrorValue = match timeout {
|
||||
let output: Option<ErrorValue> = match timeout {
|
||||
Some(timeout_duration) => tokio::time::timeout(timeout_duration, running_action)
|
||||
.await
|
||||
.map_err(|_| (JsError::Timeout, "Timed out. Retrying soon...".to_owned()))??,
|
||||
@@ -134,7 +134,7 @@ impl JsProcedure {
|
||||
.await?
|
||||
.read_only_effects()
|
||||
.run_action(name, input, self.args.clone());
|
||||
let output: ErrorValue = match timeout {
|
||||
let output: Option<ErrorValue> = match timeout {
|
||||
Some(timeout_duration) => tokio::time::timeout(timeout_duration, running_action)
|
||||
.await
|
||||
.map_err(|_| (JsError::Timeout, "Timed out. Retrying soon...".to_owned()))??,
|
||||
@@ -149,8 +149,9 @@ impl JsProcedure {
|
||||
}
|
||||
|
||||
fn unwrap_known_error<O: DeserializeOwned>(
|
||||
error_value: ErrorValue,
|
||||
error_value: Option<ErrorValue>,
|
||||
) -> Result<O, (JsError, String)> {
|
||||
let error_value = error_value.unwrap_or_else(|| ErrorValue::Result(serde_json::Value::Null));
|
||||
match error_value {
|
||||
ErrorValue::Error(error) => Err((JsError::Javascript, error)),
|
||||
ErrorValue::ErrorCode((code, message)) => Err((JsError::Code(code), message)),
|
||||
|
||||
Reference in New Issue
Block a user