Feat/logging (#2602)

* wip: Working on something to help

* chore: Add in some of the logging now

* chore: fix the type to interned instead of id

* wip

* wip

* chore: fix the logging by moving levels

* Apply suggestions from code review

* mount at machine id for journal

* Persistant

* limit log size

* feat: Actually logging and mounting now

* fix: Get the logs from the previous versions of the boot

* Chore: Add the boot id

---------

Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
Jade
2024-04-17 15:46:10 -06:00
committed by GitHub
parent 711c82472c
commit 9eff920989
10 changed files with 308 additions and 120 deletions

View File

@@ -3,38 +3,61 @@
## Methods
### init
initialize runtime (mount `/proc`, `/sys`, `/dev`, and `/run` to each image in `/media/images`)
called after os has mounted js and images to the container
#### args
`[]`
#### response
`null`
### exit
shutdown runtime
#### args
`[]`
#### response
`null`
### start
run main method if not already running
#### args
`[]`
#### response
`null`
### stop
stop main method by sending SIGTERM to child processes, and SIGKILL after timeout
#### args
`{ timeout: millis }`
#### response
`null`
### execute
run a specific package procedure
#### args
#### args
```ts
{
procedure: JsonPath,
@@ -42,12 +65,17 @@ run a specific package procedure
timeout: millis,
}
```
#### response
`any`
### sandbox
run a specific package procedure in sandbox mode
#### args
#### args
```ts
{
procedure: JsonPath,
@@ -55,5 +83,7 @@ run a specific package procedure in sandbox mode
timeout: millis,
}
```
#### response
`any`

View File

@@ -11,9 +11,13 @@ apt-get install -y curl rsync
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install 20
ln -s $(which node) /usr/bin/node
sed -i '/\(^\|#\)Storage=/c\Storage=persistent' /etc/systemd/journald.conf
sed -i '/\(^\|#\)Compress=/c\Compress=yes' /etc/systemd/journald.conf
sed -i '/\(^\|#\)SystemMaxUse=/c\SystemMaxUse=1G' /etc/systemd/journald.conf
sed -i '/\(^\|#\)ForwardToSyslog=/c\ForwardToSyslog=no' /etc/systemd/journald.conf
systemctl enable container-runtime.service
rm -rf /run/systemd

View File

@@ -5,7 +5,7 @@
"module": "./index.js",
"scripts": {
"check": "tsc --noEmit",
"build": "prettier --write '**/*.ts' && rm -rf dist && tsc",
"build": "prettier . '!tmp/**' --write && rm -rf dist && tsc",
"tsc": "rm -rf dist; tsc"
},
"author": "",

View File

@@ -16,7 +16,6 @@ use tokio::time::Instant;
use tracing::instrument;
use super::setup::CURRENT_SECRET;
use crate::account::AccountInfo;
use crate::context::config::ServerConfig;
use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation, WebSocketHandler};
use crate::db::prelude::PatchDbExt;
@@ -33,6 +32,7 @@ use crate::service::ServiceMap;
use crate::shutdown::Shutdown;
use crate::system::get_mem_info;
use crate::util::lshw::{lshw, LshwDevice};
use crate::{account::AccountInfo, lxc::ContainerId};
pub struct RpcContextSeed {
is_closed: AtomicBool,
@@ -60,7 +60,7 @@ pub struct RpcContextSeed {
}
pub struct Dev {
pub lxc: Mutex<BTreeMap<InternedString, LxcContainer>>,
pub lxc: Mutex<BTreeMap<ContainerId, LxcContainer>>,
}
pub struct Hardware {

View File

@@ -18,15 +18,21 @@ use tokio_stream::wrappers::LinesStream;
use tokio_tungstenite::tungstenite::Message;
use tracing::instrument;
use crate::context::{CliContext, RpcContext};
use crate::core::rpc_continuations::{RequestGuid, RpcContinuation};
use crate::error::ResultExt;
use crate::prelude::*;
use crate::util::serde::Reversible;
use crate::{
context::{CliContext, RpcContext},
lxc::ContainerId,
};
use crate::{
core::rpc_continuations::{RequestGuid, RpcContinuation},
util::Invoke,
};
#[pin_project::pin_project]
pub struct LogStream {
_child: Child,
_child: Option<Child>,
#[pin]
entries: BoxStream<'static, Result<JournalctlEntry, Error>>,
}
@@ -116,9 +122,11 @@ pub struct LogFollowResponse {
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LogEntry {
timestamp: DateTime<Utc>,
message: String,
boot_id: String,
}
impl std::fmt::Display for LogEntry {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
@@ -141,6 +149,8 @@ pub struct JournalctlEntry {
pub message: String,
#[serde(rename = "__CURSOR")]
pub cursor: String,
#[serde(rename = "_BOOT_ID")]
pub boot_id: String,
}
impl JournalctlEntry {
fn log_entry(self) -> Result<(String, LogEntry), Error> {
@@ -151,6 +161,7 @@ impl JournalctlEntry {
UNIX_EPOCH + Duration::from_micros(self.timestamp.parse::<u64>()?),
),
message: self.message,
boot_id: self.boot_id,
},
))
}
@@ -200,12 +211,12 @@ fn deserialize_log_message<'de, D: serde::de::Deserializer<'de>>(
/// --user-unit=UNIT Show logs from the specified user unit))
/// System: Unit is startd, but we also filter on the comm
/// Container: Filtering containers, like podman/docker is done by filtering on the CONTAINER_NAME
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum LogSource {
Kernel,
Unit(&'static str),
System,
Container(PackageId),
Container(ContainerId),
}
pub const SYSTEM_UNIT: &str = "startd";
@@ -276,7 +287,7 @@ pub async fn cli_logs(
}
}
pub async fn logs_nofollow(
_ctx: (),
ctx: RpcContext,
_: Empty,
LogsParam {
id,
@@ -286,14 +297,38 @@ pub async fn logs_nofollow(
..
}: LogsParam,
) -> Result<LogResponse, Error> {
fetch_logs(LogSource::Container(id), limit, cursor, before).await
let container_id = ctx
.services
.get(&id)
.await
.as_ref()
.map(|x| x.container_id())
.ok_or_else(|| {
Error::new(
eyre!("No service found with id: {}", id),
ErrorKind::NotFound,
)
})??;
fetch_logs(LogSource::Container(container_id), limit, cursor, before).await
}
pub async fn logs_follow(
ctx: RpcContext,
_: Empty,
LogsParam { id, limit, .. }: LogsParam,
) -> Result<LogFollowResponse, Error> {
follow_logs(ctx, LogSource::Container(id), limit).await
let container_id = ctx
.services
.get(&id)
.await
.as_ref()
.map(|x| x.container_id())
.ok_or_else(|| {
Error::new(
eyre!("No service found with id: {}", id),
ErrorKind::NotFound,
)
})??;
follow_logs(ctx, LogSource::Container(container_id), limit).await
}
pub async fn cli_logs_generic_nofollow(
@@ -358,7 +393,74 @@ pub async fn journalctl(
before: bool,
follow: bool,
) -> Result<LogStream, Error> {
let mut cmd = Command::new("journalctl");
let mut cmd = gen_journalctl_command(&id, limit);
let cursor_formatted = format!("--after-cursor={}", cursor.unwrap_or(""));
if cursor.is_some() {
cmd.arg(&cursor_formatted);
if before {
cmd.arg("--reverse");
}
}
let deserialized_entries = String::from_utf8(cmd.invoke(ErrorKind::Journald).await?)?
.lines()
.map(serde_json::from_str::<JournalctlEntry>)
.collect::<Result<Vec<_>, _>>()
.with_kind(ErrorKind::Deserialization)?;
if follow {
let mut follow_cmd = gen_journalctl_command(&id, limit);
follow_cmd.arg("-f");
if let Some(last) = deserialized_entries.last() {
cmd.arg(format!("--after-cursor={}", last.cursor));
}
let mut child = cmd.stdout(Stdio::piped()).spawn()?;
let out =
BufReader::new(child.stdout.take().ok_or_else(|| {
Error::new(eyre!("No stdout available"), crate::ErrorKind::Journald)
})?);
let journalctl_entries = LinesStream::new(out.lines());
let follow_deserialized_entries = journalctl_entries
.map_err(|e| Error::new(e, crate::ErrorKind::Journald))
.and_then(|s| {
futures::future::ready(
serde_json::from_str::<JournalctlEntry>(&s)
.with_kind(crate::ErrorKind::Deserialization),
)
});
let entries = futures::stream::iter(deserialized_entries)
.map(Ok)
.chain(follow_deserialized_entries)
.boxed();
Ok(LogStream {
_child: Some(child),
entries,
})
} else {
let entries = futures::stream::iter(deserialized_entries).map(Ok).boxed();
Ok(LogStream {
_child: None,
entries,
})
}
}
fn gen_journalctl_command(id: &LogSource, limit: usize) -> Command {
let mut cmd = match id {
LogSource::Container(container_id) => {
let mut cmd = Command::new("lxc-attach");
cmd.arg(format!("{}", container_id))
.arg("--")
.arg("journalctl");
cmd
}
_ => Command::new("journalctl"),
};
cmd.kill_on_drop(true);
cmd.arg("--output=json");
@@ -377,48 +479,11 @@ pub async fn journalctl(
cmd.arg(SYSTEM_UNIT);
cmd.arg(format!("_COMM={}", SYSTEM_UNIT));
}
LogSource::Container(id) => {
#[cfg(not(feature = "docker"))]
cmd.arg(format!("SYSLOG_IDENTIFIER={}.embassy", id));
#[cfg(feature = "docker")]
cmd.arg(format!("CONTAINER_NAME={}.embassy", id));
LogSource::Container(_container_id) => {
cmd.arg("-u").arg("container-runtime.service");
}
};
let cursor_formatted = format!("--after-cursor={}", cursor.unwrap_or(""));
if cursor.is_some() {
cmd.arg(&cursor_formatted);
if before {
cmd.arg("--reverse");
}
}
if follow {
cmd.arg("--follow");
}
let mut child = cmd.stdout(Stdio::piped()).spawn()?;
let out = BufReader::new(
child
.stdout
.take()
.ok_or_else(|| Error::new(eyre!("No stdout available"), crate::ErrorKind::Journald))?,
);
let journalctl_entries = LinesStream::new(out.lines());
let deserialized_entries = journalctl_entries
.map_err(|e| Error::new(e, crate::ErrorKind::Journald))
.and_then(|s| {
futures::future::ready(
serde_json::from_str::<JournalctlEntry>(&s)
.with_kind(crate::ErrorKind::Deserialization),
)
});
Ok(LogStream {
_child: child,
entries: deserialized_entries.boxed(),
})
cmd
}
#[instrument(skip_all)]

View File

@@ -5,9 +5,11 @@ use std::path::Path;
use std::sync::{Arc, Weak};
use std::time::Duration;
use clap::builder::ValueParserFactory;
use clap::Parser;
use futures::{AsyncWriteExt, FutureExt, StreamExt};
use imbl_value::{InOMap, InternedString};
use models::InvalidId;
use rpc_toolkit::yajrc::{RpcError, RpcResponse};
use rpc_toolkit::{
from_fn_async, AnyContext, CallRemoteHandler, GenericRpcMethod, Handler, HandlerArgs,
@@ -28,10 +30,11 @@ use crate::disk::mount::filesystem::bind::Bind;
use crate::disk::mount::filesystem::block_dev::BlockDev;
use crate::disk::mount::filesystem::idmapped::IdMapped;
use crate::disk::mount::filesystem::overlayfs::OverlayGuard;
use crate::disk::mount::filesystem::ReadWrite;
use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard};
use crate::disk::mount::filesystem::{MountType, ReadWrite};
use crate::disk::mount::guard::{GenericMountGuard, MountGuard, TmpMountGuard};
use crate::disk::mount::util::unmount;
use crate::prelude::*;
use crate::util::clap::FromStrParser;
use crate::util::rpc_client::UnixRpcClient;
use crate::util::{new_guid, Invoke};
@@ -41,18 +44,57 @@ pub const CONTAINER_RPC_SERVER_SOCKET: &str = "service.sock"; // must not be abs
pub const HOST_RPC_SERVER_SOCKET: &str = "host.sock"; // must not be absolute path
const CONTAINER_DHCP_TIMEOUT: Duration = Duration::from_secs(30);
pub struct LxcManager {
containers: Mutex<Vec<Weak<InternedString>>>,
#[derive(
Clone, Debug, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord, Hash, TS,
)]
#[ts(type = "string")]
pub struct ContainerId(InternedString);
impl std::ops::Deref for ContainerId {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::fmt::Display for ContainerId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &*self.0)
}
}
impl TryFrom<&str> for ContainerId {
type Error = InvalidId;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(ContainerId(InternedString::intern(value)))
}
}
impl std::str::FromStr for ContainerId {
type Err = InvalidId;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s)
}
}
impl ValueParserFactory for ContainerId {
type Parser = FromStrParser<Self>;
fn value_parser() -> Self::Parser {
FromStrParser::new()
}
}
#[derive(Default)]
pub struct LxcManager {
containers: Mutex<Vec<Weak<ContainerId>>>,
}
impl LxcManager {
pub fn new() -> Self {
Self {
containers: Default::default(),
}
Self::default()
}
pub async fn create(self: &Arc<Self>, config: LxcConfig) -> Result<LxcContainer, Error> {
let container = LxcContainer::new(self, config).await?;
pub async fn create(
self: &Arc<Self>,
log_mount: Option<&Path>,
config: LxcConfig,
) -> Result<LxcContainer, Error> {
let container = LxcContainer::new(self, log_mount, config).await?;
let mut guard = self.containers.lock().await;
*guard = std::mem::take(&mut *guard)
.into_iter()
@@ -69,7 +111,7 @@ impl LxcManager {
.await
.iter()
.filter_map(|g| g.upgrade())
.map(|g| (&*g).clone()),
.map(|g| (*g).clone()),
);
for container in String::from_utf8(
Command::new("lxc-ls")
@@ -80,7 +122,7 @@ impl LxcManager {
.lines()
.map(|s| s.trim())
{
if !expected.contains(container) {
if !expected.contains(&ContainerId::try_from(container)?) {
let rootfs_path = Path::new(LXC_CONTAINER_DIR).join(container).join("rootfs");
if tokio::fs::metadata(&rootfs_path).await.is_ok() {
unmount(Path::new(LXC_CONTAINER_DIR).join(container).join("rootfs")).await?;
@@ -112,14 +154,20 @@ impl LxcManager {
pub struct LxcContainer {
manager: Weak<LxcManager>,
rootfs: OverlayGuard,
guid: Arc<InternedString>,
pub guid: Arc<ContainerId>,
rpc_bind: TmpMountGuard,
log_mount: Option<MountGuard>,
config: LxcConfig,
exited: bool,
}
impl LxcContainer {
async fn new(manager: &Arc<LxcManager>, config: LxcConfig) -> Result<Self, Error> {
async fn new(
manager: &Arc<LxcManager>,
log_mount: Option<&Path>,
config: LxcConfig,
) -> Result<Self, Error> {
let guid = new_guid();
let machine_id = hex::encode(rand::random::<[u8; 16]>());
let container_dir = Path::new(LXC_CONTAINER_DIR).join(&*guid);
tokio::fs::create_dir_all(&container_dir).await?;
tokio::fs::write(
@@ -145,6 +193,7 @@ impl LxcContainer {
&rootfs_dir,
)
.await?;
tokio::fs::write(rootfs_dir.join("etc/machine-id"), format!("{machine_id}\n")).await?;
tokio::fs::write(rootfs_dir.join("etc/hostname"), format!("{guid}\n")).await?;
Command::new("sed")
.arg("-i")
@@ -166,6 +215,20 @@ impl LxcContainer {
.arg(rpc_bind.path())
.invoke(ErrorKind::Filesystem)
.await?;
let log_mount = if let Some(path) = log_mount {
let log_mount_point = rootfs_dir.join("var/log/journal").join(machine_id);
let log_mount =
MountGuard::mount(&Bind::new(path), &log_mount_point, MountType::ReadWrite).await?;
Command::new("chown")
// This was needed as 100999 because the group id of journald
.arg("100000:100999")
.arg(&log_mount_point)
.invoke(crate::ErrorKind::Filesystem)
.await?;
Some(log_mount)
} else {
None
};
Command::new("lxc-start")
.arg("-d")
.arg("--name")
@@ -175,10 +238,11 @@ impl LxcContainer {
Ok(Self {
manager: Arc::downgrade(manager),
rootfs,
guid: Arc::new(guid),
guid: Arc::new(ContainerId::try_from(&*guid)?),
rpc_bind,
config,
exited: false,
log_mount,
})
}
@@ -188,11 +252,12 @@ impl LxcContainer {
pub async fn ip(&self) -> Result<Ipv4Addr, Error> {
let start = Instant::now();
let guid: &str = &self.guid;
loop {
let output = String::from_utf8(
Command::new("lxc-info")
.arg("--name")
.arg(&*self.guid)
.arg(guid)
.arg("-iH")
.invoke(ErrorKind::Docker)
.await?,
@@ -218,6 +283,9 @@ impl LxcContainer {
#[instrument(skip_all)]
pub async fn exit(mut self) -> Result<(), Error> {
self.rpc_bind.take().unmount().await?;
if let Some(log_mount) = self.log_mount.take() {
log_mount.unmount(true).await?;
}
self.rootfs.take().unmount(true).await?;
let rootfs_path = self.rootfs_dir();
let err_path = rootfs_path.join("var/log/containerRuntime.err");
@@ -228,17 +296,16 @@ impl LxcContainer {
tracing::error!(container, "{}", line);
}
}
if tokio::fs::metadata(&rootfs_path).await.is_ok() {
if tokio_stream::wrappers::ReadDirStream::new(tokio::fs::read_dir(&rootfs_path).await?)
if tokio::fs::metadata(&rootfs_path).await.is_ok()
&& tokio_stream::wrappers::ReadDirStream::new(tokio::fs::read_dir(&rootfs_path).await?)
.count()
.await
> 0
{
return Err(Error::new(
eyre!("rootfs is not empty, refusing to delete"),
ErrorKind::InvalidRequest,
));
}
{
return Err(Error::new(
eyre!("rootfs is not empty, refusing to delete"),
ErrorKind::InvalidRequest,
));
}
Command::new("lxc-destroy")
.arg("--force")
@@ -341,21 +408,21 @@ pub fn lxc() -> ParentHandler {
.subcommand("connect", from_fn_async(connect_rpc_cli).no_display())
}
pub async fn create(ctx: RpcContext) -> Result<InternedString, Error> {
let container = ctx.lxc_manager.create(LxcConfig::default()).await?;
pub async fn create(ctx: RpcContext) -> Result<ContainerId, Error> {
let container = ctx.lxc_manager.create(None, LxcConfig::default()).await?;
let guid = container.guid.deref().clone();
ctx.dev.lxc.lock().await.insert(guid.clone(), container);
Ok(guid)
}
pub async fn list(ctx: RpcContext) -> Result<Vec<InternedString>, Error> {
pub async fn list(ctx: RpcContext) -> Result<Vec<ContainerId>, Error> {
Ok(ctx.dev.lxc.lock().await.keys().cloned().collect())
}
#[derive(Deserialize, Serialize, Parser, TS)]
pub struct RemoveParams {
#[ts(type = "string")]
pub guid: InternedString,
pub guid: ContainerId,
}
pub async fn remove(ctx: RpcContext, RemoveParams { guid }: RemoveParams) -> Result<(), Error> {
@@ -368,7 +435,7 @@ pub async fn remove(ctx: RpcContext, RemoveParams { guid }: RemoveParams) -> Res
#[derive(Deserialize, Serialize, Parser, TS)]
pub struct ConnectParams {
#[ts(type = "string")]
pub guid: InternedString,
pub guid: ContainerId,
}
pub async fn connect_rpc(
@@ -502,7 +569,7 @@ pub async fn connect_cli(ctx: &CliContext, guid: RequestGuid) -> Result<(), Erro
if let Some((method, rest)) = command.split_first() {
let mut params = InOMap::new();
for arg in rest {
if let Some((name, value)) = arg.split_once("=") {
if let Some((name, value)) = arg.split_once('=') {
params.insert(InternedString::intern(name), if value.is_empty() {
Value::Null
} else if let Ok(v) = serde_json::from_str(value) {

View File

@@ -70,7 +70,7 @@ fn filter(p: &Path) -> bool {
#[derive(Clone)]
pub struct S9pk<S = Section<MultiCursorFile>> {
manifest: Manifest,
pub manifest: Manifest,
manifest_dirty: bool,
archive: MerkleArchive<S>,
size: Option<u64>,

View File

@@ -13,7 +13,6 @@ use start_stop::StartStop;
use tokio::sync::Notify;
use ts_rs::TS;
use crate::context::{CliContext, RpcContext};
use crate::core::rpc_continuations::RequestGuid;
use crate::db::model::package::{
InstalledState, PackageDataEntry, PackageState, PackageStateMatchModelRef, UpdatingState,
@@ -32,6 +31,10 @@ use crate::util::actor::concurrent::ConcurrentActor;
use crate::util::actor::Actor;
use crate::util::serde::Pem;
use crate::volume::data_dir;
use crate::{
context::{CliContext, RpcContext},
lxc::ContainerId,
};
mod action;
pub mod cli;
@@ -193,8 +196,8 @@ impl Service {
|db| {
db.as_public_mut()
.as_package_data_mut()
.as_idx_mut(&id)
.or_not_found(&id)?
.as_idx_mut(id)
.or_not_found(id)?
.as_state_info_mut()
.map_mutate(|s| {
if let PackageState::Updating(UpdatingState {
@@ -223,16 +226,12 @@ impl Service {
tracing::debug!("{e:?}")
})
{
if service
.uninstall(None)
.await
.map_err(|e| {
match service.uninstall(None).await {
Err(e) => {
tracing::error!("Error uninstalling service: {e}");
tracing::debug!("{e:?}")
})
.is_ok()
{
return Ok(None);
}
Ok(()) => return Ok(None),
}
}
}
@@ -299,10 +298,10 @@ impl Service {
}
pub async fn restore(
ctx: RpcContext,
s9pk: S9pk,
guard: impl GenericMountGuard,
progress: Option<InstallProgressHandles>,
_ctx: RpcContext,
_s9pk: S9pk,
_guard: impl GenericMountGuard,
_progress: Option<InstallProgressHandles>,
) -> Result<Self, Error> {
// TODO
Err(Error::new(eyre!("not yet implemented"), ErrorKind::Unknown))
@@ -341,21 +340,36 @@ impl Service {
.execute(ProcedureName::Uninit, to_value(&target_version)?, None) // TODO timeout
.await?;
let id = self.seed.persistent_container.s9pk.as_manifest().id.clone();
self.seed
.ctx
.db
.mutate(|d| d.as_public_mut().as_package_data_mut().remove(&id))
.await?;
self.shutdown().await
let ctx = self.seed.ctx.clone();
self.shutdown().await?;
if target_version.is_none() {
ctx.db
.mutate(|d| d.as_public_mut().as_package_data_mut().remove(&id))
.await?;
}
Ok(())
}
pub async fn backup(&self, _guard: impl GenericMountGuard) -> Result<BackupReturn, Error> {
// TODO
Err(Error::new(eyre!("not yet implemented"), ErrorKind::Unknown))
}
pub fn container_id(&self) -> Result<ContainerId, Error> {
let id = &self.seed.id;
let container_id = (*self
.seed
.persistent_container
.lxc_container
.get()
.or_not_found(format!("container for {id}"))?
.guid)
.clone();
Ok(container_id)
}
}
#[derive(Debug, Clone)]
struct RunningStatus {
pub struct RunningStatus {
health: OrdMap<HealthCheckId, HealthCheckResult>,
started: DateTime<Utc>,
}

View File

@@ -10,7 +10,7 @@ use imbl_value::InternedString;
use models::{ProcedureName, VolumeId};
use rpc_toolkit::{Empty, Server, ShutdownHandle};
use serde::de::DeserializeOwned;
use tokio::fs::File;
use tokio::fs::{create_dir_all, File};
use tokio::process::Command;
use tokio::sync::{oneshot, watch, Mutex, OnceCell};
use tracing::instrument;
@@ -39,8 +39,6 @@ use crate::ARCH;
const RPC_CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
struct ProcedureId(u64);
#[derive(Debug)]
pub struct ServiceState {
// This contains the start time and health check information for when the service is running. Note: Will be overwritting to the db,
@@ -99,7 +97,17 @@ pub struct PersistentContainer {
impl PersistentContainer {
#[instrument(skip_all)]
pub async fn new(ctx: &RpcContext, s9pk: S9pk, start: StartStop) -> Result<Self, Error> {
let lxc_container = ctx.lxc_manager.create(LxcConfig::default()).await?;
let lxc_container = ctx
.lxc_manager
.create(
Some(
&ctx.datadir
.join("package-data/logs")
.join(&s9pk.as_manifest().id),
),
LxcConfig::default(),
)
.await?;
let rpc_client = lxc_container.connect_rpc(Some(RPC_CONNECT_TIMEOUT)).await?;
let js_mount = MountGuard::mount(
&LoopDev::from(
@@ -114,6 +122,7 @@ impl PersistentContainer {
ReadOnly,
)
.await?;
let mut volumes = BTreeMap::new();
for volume in &s9pk.as_manifest().volumes {
let mountpoint = lxc_container
@@ -175,7 +184,7 @@ impl PersistentContainer {
if let Some(env) = s9pk
.as_archive()
.contents()
.get_path(Path::new("images").join(&*ARCH).join(&env_filename))
.get_path(Path::new("images").join(*ARCH).join(&env_filename))
.and_then(|e| e.as_file())
{
env.copy(&mut File::create(image_path.join(&env_filename)).await?)
@@ -185,7 +194,7 @@ impl PersistentContainer {
if let Some(json) = s9pk
.as_archive()
.contents()
.get_path(Path::new("images").join(&*ARCH).join(&json_filename))
.get_path(Path::new("images").join(*ARCH).join(&json_filename))
.and_then(|e| e.as_file())
{
json.copy(&mut File::create(image_path.join(&json_filename)).await?)
@@ -231,7 +240,7 @@ impl PersistentContainer {
.join(HOST_RPC_SERVER_SOCKET);
let (send, recv) = oneshot::channel();
let handle = NonDetachingJoinHandle::from(tokio::spawn(async move {
let (shutdown, fut) = match async {
let chown_status = async {
let res = server.run_unix(&path, |err| {
tracing::error!("error on unix socket {}: {err}", path.display())
})?;
@@ -241,9 +250,8 @@ impl PersistentContainer {
.invoke(ErrorKind::Filesystem)
.await?;
Ok::<_, Error>(res)
}
.await
{
};
let (shutdown, fut) = match chown_status.await {
Ok((shutdown, fut)) => (Ok(shutdown), Some(fut)),
Err(e) => (Err(e), None),
};

View File

@@ -32,9 +32,9 @@ use crate::util::serde::Pem;
pub type DownloadInstallFuture = BoxFuture<'static, Result<InstallFuture, Error>>;
pub type InstallFuture = BoxFuture<'static, Result<(), Error>>;
pub(super) struct InstallProgressHandles {
pub(super) finalization_progress: PhaseProgressTrackerHandle,
pub(super) progress_handle: FullProgressTrackerHandle,
pub struct InstallProgressHandles {
pub finalization_progress: PhaseProgressTrackerHandle,
pub progress_handle: FullProgressTrackerHandle,
}
/// This is the structure to contain all the services