addHealthCheck instead of additionalHealthChecks for Daemons (#2962)

* addHealthCheck on Daemons

* fix bug that prevents domains without protocols from being deleted

* fixes from testing

* version bump

* add sdk version to UI

* fix useEntrypoint

* fix dependency health check error display

* minor fixes

* beta.29

* fixes from testing

* beta.30

* set /etc/os-release (#2918)

* remove check-monitor from kiosk (#2059)

* add units for progress (#2693)

* use new progress type

* alpha.7

* fix up pwa stuff

* fix wormhole-squashfs and prune boot (#2964)

* don't exit on expected errors

* use bash

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
Aiden McClelland
2025-06-17 23:50:01 +00:00
committed by GitHub
parent f5688e077a
commit 3ec4db0225
100 changed files with 846 additions and 757 deletions

2
core/Cargo.lock generated
View File

@@ -5975,7 +5975,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "start-os"
version = "0.4.0-alpha.6"
version = "0.4.0-alpha.7"
dependencies = [
"aes 0.7.5",
"async-acme",

View File

@@ -1,164 +0,0 @@
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use color_eyre::eyre::bail;
use container_init::{Input, Output, ProcessId, RpcId};
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
use tokio::sync::Mutex;
/// Used by the js-executor, it is the ability to just create a command in an already running exec
pub type ExecCommand = Arc<
dyn Fn(
String,
Vec<String>,
UnboundedSender<container_init::Output>,
Option<Duration>,
) -> Pin<Box<dyn Future<Output = Result<RpcId, String>> + 'static>>
+ Send
+ Sync
+ 'static,
>;
/// Used by the js-executor, it is the ability to just create a command in an already running exec
pub type SendKillSignal = Arc<
dyn Fn(RpcId, u32) -> Pin<Box<dyn Future<Output = Result<(), String>> + 'static>>
+ Send
+ Sync
+ 'static,
>;
pub trait CommandInserter {
fn insert_command(
&self,
command: String,
args: Vec<String>,
sender: UnboundedSender<container_init::Output>,
timeout: Option<Duration>,
) -> Pin<Box<dyn Future<Output = Option<RpcId>>>>;
fn send_signal(&self, id: RpcId, command: u32) -> Pin<Box<dyn Future<Output = ()>>>;
}
pub type ArcCommandInserter = Arc<Mutex<Option<Box<dyn CommandInserter>>>>;
pub struct ExecutingCommand {
rpc_id: RpcId,
/// Will exist until killed
command_inserter: Arc<Mutex<Option<ArcCommandInserter>>>,
owned_futures: Arc<Mutex<Vec<Pin<Box<dyn Future<Output = ()>>>>>>,
}
impl ExecutingCommand {
pub async fn new(
command_inserter: ArcCommandInserter,
command: String,
args: Vec<String>,
timeout: Option<Duration>,
) -> Result<ExecutingCommand, color_eyre::Report> {
let (sender, receiver) = tokio::sync::mpsc::unbounded_channel::<Output>();
let rpc_id = {
let locked_command_inserter = command_inserter.lock().await;
let locked_command_inserter = match &*locked_command_inserter {
Some(a) => a,
None => bail!("Expecting containers.main in the package manifest".to_string()),
};
match locked_command_inserter
.insert_command(command, args, sender, timeout)
.await
{
Some(a) => a,
None => bail!("Couldn't get command started ".to_string()),
}
};
let executing_commands = ExecutingCommand {
rpc_id,
command_inserter: Arc::new(Mutex::new(Some(command_inserter.clone()))),
owned_futures: Default::default(),
};
// let waiting = self.wait()
Ok(executing_commands)
}
async fn wait(
rpc_id: RpcId,
mut outputs: UnboundedReceiver<Output>,
) -> Result<String, (Option<i32>, String)> {
let (process_id_send, process_id_recv) = tokio::sync::oneshot::channel::<ProcessId>();
let mut answer = String::new();
let mut command_error = String::new();
let mut status: Option<i32> = None;
let mut process_id_send = Some(process_id_send);
while let Some(output) = outputs.recv().await {
match output {
Output::ProcessId(process_id) => {
if let Some(process_id_send) = process_id_send.take() {
if let Err(err) = process_id_send.send(process_id) {
tracing::error!(
"Could not get a process id {process_id:?} sent for {rpc_id:?}"
);
tracing::debug!("{err:?}");
}
}
}
Output::Line(value) => {
answer.push_str(&value);
answer.push('\n');
}
Output::Error(error) => {
command_error.push_str(&error);
command_error.push('\n');
}
Output::Done(error_code) => {
status = error_code;
break;
}
}
}
if !command_error.is_empty() {
return Err((status, command_error));
}
Ok(answer)
}
async fn send_signal(&self, signal: u32) {
let locked = self.command_inserter.lock().await;
let inner = match &*locked {
Some(a) => a,
None => return,
};
let locked = inner.lock().await;
let command_inserter = match &*locked {
Some(a) => a,
None => return,
};
command_inserter.send_signal(self.rpc_id, signal);
}
/// Should only be called when output::done
async fn killed(&self) {
*self.owned_futures.lock().await = Default::default();
*self.command_inserter.lock().await = Default::default();
}
pub fn rpc_id(&self) -> RpcId {
self.rpc_id
}
}
impl Drop for ExecutingCommand {
fn drop(&mut self) {
let command_inserter = self.command_inserter.clone();
let rpc_id = self.rpc_id.clone();
tokio::spawn(async move {
let command_inserter_lock = command_inserter.lock().await;
let command_inserter = match &*command_inserter_lock {
Some(a) => a,
None => {
return;
}
};
command_inserter.send_kill_command(rpc_id, 9).await;
});
}
}

View File

@@ -14,7 +14,7 @@ keywords = [
name = "start-os"
readme = "README.md"
repository = "https://github.com/Start9Labs/start-os"
version = "0.4.0-alpha.6" # VERSION_BUMP
version = "0.4.0-alpha.7" # VERSION_BUMP
license = "MIT"
[lib]

View File

@@ -252,14 +252,18 @@ impl fmt::Display for ActionResultV1 {
}
}
pub fn display_action_result<T: Serialize>(params: WithIoFormat<T>, result: Option<ActionResult>) {
pub fn display_action_result<T: Serialize>(
params: WithIoFormat<T>,
result: Option<ActionResult>,
) -> Result<(), Error> {
let Some(result) = result else {
return;
return Ok(());
};
if let Some(format) = params.format {
return display_serializable(format, result);
}
println!("{result}")
println!("{result}");
Ok(())
}
#[derive(Deserialize, Serialize, TS)]

View File

@@ -328,9 +328,7 @@ pub fn session<C: Context>() -> ParentHandler<C> {
from_fn_async(list)
.with_metadata("get_session", Value::Bool(true))
.with_display_serializable()
.with_custom_display_fn(|handle, result| {
Ok(display_sessions(handle.params, result))
})
.with_custom_display_fn(|handle, result| display_sessions(handle.params, result))
.with_about("Display all server sessions")
.with_call_remote::<CliContext>(),
)
@@ -343,7 +341,7 @@ pub fn session<C: Context>() -> ParentHandler<C> {
)
}
fn display_sessions(params: WithIoFormat<ListParams>, arg: SessionList) {
fn display_sessions(params: WithIoFormat<ListParams>, arg: SessionList) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -371,7 +369,8 @@ fn display_sessions(params: WithIoFormat<ListParams>, arg: SessionList) {
}
table.add_row(row);
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
}
#[derive(Deserialize, Serialize, Parser, TS)]

View File

@@ -20,6 +20,7 @@ use crate::disk::mount::filesystem::ReadWrite;
use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard};
use crate::init::init;
use crate::prelude::*;
use crate::progress::ProgressUnits;
use crate::s9pk::S9pk;
use crate::service::service_map::DownloadInstallFuture;
use crate::setup::SetupExecuteProgress;
@@ -136,6 +137,7 @@ pub async fn recover_full_embassy(
.collect();
let tasks = restore_packages(&rpc_ctx, backup_guard, ids).await?;
restore_phase.set_total(tasks.len() as u64);
restore_phase.set_units(Some(ProgressUnits::Steps));
let restore_phase = Arc::new(Mutex::new(restore_phase));
stream::iter(tasks)
.for_each_concurrent(5, |(id, res)| {

View File

@@ -157,7 +157,7 @@ pub fn target<C: Context>() -> ParentHandler<C> {
from_fn_async(info)
.with_display_serializable()
.with_custom_display_fn::<CliContext, _>(|params, info| {
Ok(display_backup_info(params.params, info))
display_backup_info(params.params, info)
})
.with_about("Display package backup information")
.with_call_remote::<CliContext>(),
@@ -227,7 +227,7 @@ pub struct PackageBackupInfo {
pub timestamp: DateTime<Utc>,
}
fn display_backup_info(params: WithIoFormat<InfoParams>, info: BackupInfo) {
fn display_backup_info(params: WithIoFormat<InfoParams>, info: BackupInfo) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -260,7 +260,8 @@ fn display_backup_info(params: WithIoFormat<InfoParams>, info: BackupInfo) {
];
table.add_row(row);
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
}
#[derive(Deserialize, Serialize, Parser, TS)]

View File

@@ -48,9 +48,7 @@ pub fn disk<C: Context>() -> ParentHandler<C> {
"list",
from_fn_async(list)
.with_display_serializable()
.with_custom_display_fn(|handle, result| {
Ok(display_disk_info(handle.params, result))
})
.with_custom_display_fn(|handle, result| display_disk_info(handle.params, result))
.with_about("List disk info")
.with_call_remote::<CliContext>(),
)
@@ -65,7 +63,7 @@ pub fn disk<C: Context>() -> ParentHandler<C> {
)
}
fn display_disk_info(params: WithIoFormat<Empty>, args: Vec<DiskInfo>) {
fn display_disk_info(params: WithIoFormat<Empty>, args: Vec<DiskInfo>) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -124,7 +122,8 @@ fn display_disk_info(params: WithIoFormat<Empty>, args: Vec<DiskInfo>) {
table.add_row(row);
}
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
}
// #[command(display(display_disk_info))]

View File

@@ -32,7 +32,7 @@ use crate::net::utils::find_wifi_iface;
use crate::net::web_server::{UpgradableListener, WebServerAcceptorSetter};
use crate::prelude::*;
use crate::progress::{
FullProgress, FullProgressTracker, PhaseProgressTrackerHandle, PhasedProgressBar,
FullProgress, FullProgressTracker, PhaseProgressTrackerHandle, PhasedProgressBar, ProgressUnits,
};
use crate::rpc_continuations::{Guid, RpcContinuation};
use crate::s9pk::v2::pack::{CONTAINER_DATADIR, CONTAINER_TOOL};
@@ -259,6 +259,7 @@ pub async fn run_script<P: AsRef<Path>>(path: P, mut progress: PhaseProgressTrac
if let Err(e) = async {
let script = tokio::fs::read_to_string(script).await?;
progress.set_total(script.as_bytes().iter().filter(|b| **b == b'\n').count() as u64);
progress.set_units(Some(ProgressUnits::Bytes));
let mut reader = IOHook::new(Cursor::new(script.as_bytes()));
reader.post_read(|buf| progress += buf.iter().filter(|b| **b == b'\n').count() as u64);
Command::new("/bin/bash")

View File

@@ -89,7 +89,7 @@ use crate::context::{
use crate::disk::fsck::RequiresReboot;
use crate::registry::context::{RegistryContext, RegistryUrlParams};
use crate::system::kiosk;
use crate::util::serde::{HandlerExtSerde, WithIoFormat};
use crate::util::serde::{display_serializable, HandlerExtSerde, WithIoFormat};
#[derive(Deserialize, Serialize, Parser, TS)]
#[serde(rename_all = "camelCase")]
@@ -201,15 +201,6 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
if &*PLATFORM != "raspberrypi" {
api = api.subcommand("kiosk", kiosk::<C>());
}
#[cfg(feature = "dev")]
{
api = api.subcommand(
"lxc",
lxc::dev::lxc::<C>().with_about(
"Commands related to lxc containers i.e. create, list, remove, connect",
),
);
}
api
}
@@ -220,7 +211,7 @@ pub fn server<C: Context>() -> ParentHandler<C> {
from_fn_async(system::time)
.with_display_serializable()
.with_custom_display_fn(|handle, result| {
Ok(system::display_time(handle.params, result))
system::display_time(handle.params, result)
})
.with_about("Display current time and server uptime")
.with_call_remote::<CliContext>()
@@ -416,6 +407,46 @@ pub fn package<C: Context>() -> ParentHandler<C> {
.with_about("Rebuild service container")
.with_call_remote::<CliContext>(),
)
.subcommand(
"stats",
from_fn_async(lxc::stats)
.with_display_serializable()
.with_custom_display_fn(|args, res| {
if let Some(format) = args.params.format {
return display_serializable(format, res);
}
use prettytable::*;
let mut table = table!([
"Name",
"Container ID",
"Memory Usage",
"Memory Limit",
"Memory %"
]);
for (id, stats) in res {
if let Some(stats) = stats {
table.add_row(row![
&*id,
&*stats.container_id,
stats.memory_usage,
stats.memory_limit,
format!(
"{:.2}",
stats.memory_usage.0 as f64 / stats.memory_limit.0 as f64
* 100.0
)
]);
} else {
table.add_row(row![&*id, "N/A", "0 MiB", "0 MiB", "0"]);
}
}
table.print_tty(false)?;
Ok(())
})
.with_about("List information related to the lxc containers i.e. CPU, Memory, Disk")
.with_call_remote::<CliContext>(),
)
.subcommand("logs", logs::package_logs())
.subcommand(
"logs",

View File

@@ -1,174 +0,0 @@
use std::ops::Deref;
use clap::Parser;
use rpc_toolkit::{
from_fn_async, CallRemoteHandler, Context, Empty, HandlerArgs, HandlerExt, HandlerFor,
ParentHandler,
};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use crate::context::{CliContext, RpcContext};
use crate::lxc::{ContainerId, LxcConfig};
use crate::prelude::*;
use crate::rpc_continuations::Guid;
use crate::service::ServiceStats;
pub fn lxc<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
"create",
from_fn_async(create)
.with_about("Create lxc container")
.with_call_remote::<CliContext>(),
)
.subcommand(
"list",
from_fn_async(list)
.with_custom_display_fn(|_, res| {
use prettytable::*;
let mut table = table!([bc => "GUID"]);
for guid in res {
table.add_row(row![&*guid]);
}
table.printstd();
Ok(())
})
.with_about("List lxc containers")
.with_call_remote::<CliContext>(),
)
.subcommand(
"stats",
from_fn_async(stats)
.with_custom_display_fn(|_, res| {
use prettytable::*;
let mut table = table!([
"Container ID",
"Name",
"Memory Usage",
"Memory Limit",
"Memory %"
]);
for ServiceStats {
container_id,
package_id,
memory_usage,
memory_limit,
} in res
{
table.add_row(row![
&*container_id,
&*package_id,
memory_usage,
memory_limit,
format!(
"{:.2}",
memory_usage.0 as f64 / memory_limit.0 as f64 * 100.0
)
]);
}
table.printstd();
Ok(())
})
.with_about("List information related to the lxc containers i.e. CPU, Memory, Disk")
.with_call_remote::<CliContext>(),
)
.subcommand(
"remove",
from_fn_async(remove)
.no_display()
.with_about("Remove lxc container")
.with_call_remote::<CliContext>(),
)
.subcommand("connect", from_fn_async(connect_rpc).no_cli())
.subcommand(
"connect",
from_fn_async(connect_rpc_cli)
.no_display()
.with_about("Connect to a lxc container"),
)
}
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<ContainerId>, Error> {
Ok(ctx.dev.lxc.lock().await.keys().cloned().collect())
}
pub async fn stats(ctx: RpcContext) -> Result<Vec<ServiceStats>, Error> {
let ids = ctx.db.peek().await.as_public().as_package_data().keys()?;
let guids: Vec<_> = ctx.dev.lxc.lock().await.keys().cloned().collect();
let mut stats = Vec::with_capacity(guids.len());
for id in ids {
let service: tokio::sync::OwnedRwLockReadGuard<Option<crate::service::ServiceRef>> =
ctx.services.get(&id).await;
let service_ref = service.as_ref().or_not_found(&id)?;
stats.push(service_ref.stats().await?);
}
Ok(stats)
}
#[derive(Deserialize, Serialize, Parser, TS)]
pub struct RemoveParams {
#[ts(type = "string")]
pub guid: ContainerId,
}
pub async fn remove(ctx: RpcContext, RemoveParams { guid }: RemoveParams) -> Result<(), Error> {
if let Some(container) = ctx.dev.lxc.lock().await.remove(&guid) {
container.exit().await?;
}
Ok(())
}
#[derive(Deserialize, Serialize, Parser, TS)]
pub struct ConnectParams {
#[ts(type = "string")]
pub guid: ContainerId,
}
pub async fn connect_rpc(
ctx: RpcContext,
ConnectParams { guid }: ConnectParams,
) -> Result<Guid, Error> {
super::connect(
&ctx,
ctx.dev.lxc.lock().await.get(&guid).ok_or_else(|| {
Error::new(eyre!("No container with guid: {guid}"), ErrorKind::NotFound)
})?,
)
.await
}
pub async fn connect_rpc_cli(
HandlerArgs {
context,
parent_method,
method,
params,
inherited_params,
raw_params,
}: HandlerArgs<CliContext, ConnectParams>,
) -> Result<(), Error> {
let ctx = context.clone();
let guid = CallRemoteHandler::<CliContext, _, _>::new(from_fn_async(connect_rpc))
.handle_async(HandlerArgs {
context,
parent_method,
method,
params: rpc_toolkit::util::Flat(params, Empty {}),
inherited_params,
raw_params,
})
.await?;
super::connect_cli(&ctx, guid).await
}

View File

@@ -1,4 +1,4 @@
use std::collections::BTreeSet;
use std::collections::{BTreeMap, BTreeSet};
use std::net::Ipv4Addr;
use std::path::Path;
use std::sync::{Arc, Weak};
@@ -7,7 +7,7 @@ use std::time::Duration;
use clap::builder::ValueParserFactory;
use futures::{AsyncWriteExt, StreamExt};
use imbl_value::{InOMap, InternedString};
use models::{FromStrParser, InvalidId};
use models::{FromStrParser, InvalidId, PackageId};
use rpc_toolkit::yajrc::RpcError;
use rpc_toolkit::{GenericRpcMethod, RpcRequest, RpcResponse};
use rustyline_async::{ReadlineEvent, SharedWriter};
@@ -28,13 +28,11 @@ use crate::disk::mount::guard::{GenericMountGuard, MountGuard, TmpMountGuard};
use crate::disk::mount::util::unmount;
use crate::prelude::*;
use crate::rpc_continuations::{Guid, RpcContinuation};
use crate::service::ServiceStats;
use crate::util::io::open_file;
use crate::util::rpc_client::UnixRpcClient;
use crate::util::{new_guid, Invoke};
// #[cfg(feature = "dev")]
pub mod dev;
const LXC_CONTAINER_DIR: &str = "/var/lib/lxc";
const RPC_DIR: &str = "media/startos/rpc"; // must not be absolute path
pub const CONTAINER_RPC_SERVER_SOCKET: &str = "service.sock"; // must not be absolute path
@@ -564,3 +562,21 @@ pub async fn connect_cli(ctx: &CliContext, guid: Guid) -> Result<(), Error> {
Ok(())
}
pub async fn stats(ctx: RpcContext) -> Result<BTreeMap<PackageId, Option<ServiceStats>>, Error> {
let ids = ctx.db.peek().await.as_public().as_package_data().keys()?;
let mut stats = BTreeMap::new();
for id in ids {
let service: tokio::sync::OwnedRwLockReadGuard<Option<crate::service::ServiceRef>> =
ctx.services.get(&id).await;
let Some(service_ref) = service.as_ref() else {
stats.insert(id, None);
continue;
};
stats.insert(id, Some(service_ref.stats().await?));
}
Ok(stats)
}

View File

@@ -159,7 +159,7 @@ pub fn binding<C: Context, Kind: HostApiKind>(
use prettytable::*;
if let Some(format) = params.format {
return Ok(display_serializable(format, res));
return display_serializable(format, res);
}
let mut table = Table::new();
@@ -182,7 +182,7 @@ pub fn binding<C: Context, Kind: HostApiKind>(
]);
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
})

View File

@@ -47,7 +47,7 @@ pub fn network_interface_api<C: Context>() -> ParentHandler<C> {
use prettytable::*;
if let Some(format) = params.format {
return Ok(display_serializable(format, res));
return display_serializable(format, res);
}
let mut table = Table::new();
@@ -78,7 +78,7 @@ pub fn network_interface_api<C: Context>() -> ParentHandler<C> {
]);
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
})

View File

@@ -90,9 +90,7 @@ pub fn tor<C: Context>() -> ParentHandler<C> {
"list-services",
from_fn_async(list_services)
.with_display_serializable()
.with_custom_display_fn(|handle, result| {
Ok(display_services(handle.params, result))
})
.with_custom_display_fn(|handle, result| display_services(handle.params, result))
.with_about("Display Tor V3 Onion Addresses")
.with_call_remote::<CliContext>(),
)
@@ -210,7 +208,10 @@ pub async fn reset(
.await
}
pub fn display_services(params: WithIoFormat<Empty>, services: Vec<OnionAddressV3>) {
pub fn display_services(
params: WithIoFormat<Empty>,
services: Vec<OnionAddressV3>,
) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -222,7 +223,8 @@ pub fn display_services(params: WithIoFormat<Empty>, services: Vec<OnionAddressV
let row = row![&service.to_string()];
table.add_row(row);
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
}
pub async fn list_services(ctx: RpcContext, _: Empty) -> Result<Vec<OnionAddressV3>, Error> {

View File

@@ -70,9 +70,7 @@ pub fn wifi<C: Context>() -> ParentHandler<C> {
"get",
from_fn_async(get)
.with_display_serializable()
.with_custom_display_fn(|handle, result| {
Ok(display_wifi_info(handle.params, result))
})
.with_custom_display_fn(|handle, result| display_wifi_info(handle.params, result))
.with_about("List wifi info")
.with_call_remote::<CliContext>(),
)
@@ -134,7 +132,7 @@ pub fn available<C: Context>() -> ParentHandler<C> {
"get",
from_fn_async(get_available)
.with_display_serializable()
.with_custom_display_fn(|handle, result| Ok(display_wifi_list(handle.params, result)))
.with_custom_display_fn(|handle, result| display_wifi_list(handle.params, result))
.with_about("List available wifi networks")
.with_call_remote::<CliContext>(),
)
@@ -363,7 +361,7 @@ pub struct WifiListOut {
security: Vec<String>,
}
pub type WifiList = HashMap<Ssid, WifiListInfoLow>;
fn display_wifi_info(params: WithIoFormat<Empty>, info: WifiListInfo) {
fn display_wifi_info(params: WithIoFormat<Empty>, info: WifiListInfo) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -424,10 +422,11 @@ fn display_wifi_info(params: WithIoFormat<Empty>, info: WifiListInfo) {
]);
}
table_global.print_tty(false).unwrap();
table_global.print_tty(false)?;
Ok(())
}
fn display_wifi_list(params: WithIoFormat<Empty>, info: Vec<WifiListOut>) {
fn display_wifi_list(params: WithIoFormat<Empty>, info: Vec<WifiListOut>) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -448,7 +447,8 @@ fn display_wifi_list(params: WithIoFormat<Empty>, info: Vec<WifiListOut>) {
]);
}
table_global.print_tty(false).unwrap();
table_global.print_tty(false)?;
Ok(())
}
// #[command(display(display_wifi_info))]

View File

@@ -24,6 +24,13 @@ lazy_static::lazy_static! {
static ref BYTES: ProgressStyle = ProgressStyle::with_template("{spinner} {wide_msg} [{bytes}/?] [{binary_bytes_per_sec} {elapsed}]").unwrap();
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, TS)]
#[serde(rename_all = "kebab-case")]
pub enum ProgressUnits {
Bytes,
Steps,
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, TS)]
#[serde(untagged)]
pub enum Progress {
@@ -34,13 +41,14 @@ pub enum Progress {
done: u64,
#[ts(type = "number | null")]
total: Option<u64>,
units: Option<ProgressUnits>,
},
}
impl Progress {
pub fn new() -> Self {
Progress::NotStarted(())
}
pub fn update_bar(self, bar: &ProgressBar, bytes: bool) {
pub fn update_bar(self, bar: &ProgressBar) {
match self {
Self::NotStarted(()) => {
bar.set_style(SPINNER.clone());
@@ -52,8 +60,12 @@ impl Progress {
Self::Complete(true) => {
bar.finish();
}
Self::Progress { done, total: None } => {
if bytes {
Self::Progress {
done,
total: None,
units,
} => {
if units == Some(ProgressUnits::Bytes) {
bar.set_style(BYTES.clone());
} else {
bar.set_style(STEPS.clone());
@@ -64,8 +76,9 @@ impl Progress {
Self::Progress {
done,
total: Some(total),
units,
} => {
if bytes {
if units == Some(ProgressUnits::Bytes) {
bar.set_style(PERCENTAGE_BYTES.clone());
} else {
bar.set_style(PERCENTAGE.clone());
@@ -84,14 +97,22 @@ impl Progress {
}
pub fn set_done(&mut self, done: u64) {
*self = match *self {
Self::Complete(false) | Self::NotStarted(()) => Self::Progress { done, total: None },
Self::Progress { mut done, total } => {
Self::Complete(false) | Self::NotStarted(()) => Self::Progress {
done,
total: None,
units: None,
},
Self::Progress {
mut done,
total,
units,
} => {
if let Some(total) = total {
if done > total {
done = total;
}
}
Self::Progress { done, total }
Self::Progress { done, total, units }
}
Self::Complete(true) => Self::Complete(true),
};
@@ -101,10 +122,12 @@ impl Progress {
Self::Complete(false) | Self::NotStarted(()) => Self::Progress {
done: 0,
total: Some(total),
units: None,
},
Self::Progress { done, .. } => Self::Progress {
Self::Progress { done, units, .. } => Self::Progress {
done,
total: Some(total),
units,
},
Self::Complete(true) => Self::Complete(true),
}
@@ -113,17 +136,30 @@ impl Progress {
if let Self::Progress {
done,
total: Some(old),
units,
} = *self
{
*self = Self::Progress {
done,
total: Some(old + total),
units,
};
} else {
self.set_total(total)
}
}
pub fn complete(&mut self) {
pub fn set_units(&mut self, units: Option<ProgressUnits>) {
*self = match *self {
Self::Complete(false) | Self::NotStarted(()) => Self::Progress {
done: 0,
total: None,
units,
},
Self::Progress { done, total, .. } => Self::Progress { done, total, units },
Self::Complete(true) => Self::Complete(true),
};
}
pub fn set_complete(&mut self) {
*self = Self::Complete(true);
}
pub fn is_complete(&self) -> bool {
@@ -137,15 +173,16 @@ impl std::ops::Add<u64> for Progress {
Self::Complete(false) | Self::NotStarted(()) => Self::Progress {
done: rhs,
total: None,
units: None,
},
Self::Progress { done, total } => {
Self::Progress { done, total, units } => {
let mut done = done + rhs;
if let Some(total) = total {
if done > total {
done = total;
}
}
Self::Progress { done, total }
Self::Progress { done, total, units }
}
Self::Complete(true) => Self::Complete(true),
}
@@ -337,7 +374,7 @@ impl FullProgressTracker {
}
}
pub fn complete(&self) {
self.overall.send_modify(|o| o.complete());
self.overall.send_modify(|o| o.set_complete());
}
}
@@ -355,6 +392,7 @@ impl PhaseProgressTrackerHandle {
Progress::Progress {
done,
total: Some(total),
..
} => ((done as f64 / total as f64) * overall_contribution as f64) as u64,
_ => 0,
};
@@ -380,8 +418,11 @@ impl PhaseProgressTrackerHandle {
self.progress.send_modify(|p| p.add_total(total));
self.update_overall();
}
pub fn set_units(&mut self, units: Option<ProgressUnits>) {
self.progress.send_modify(|p| p.set_units(units));
}
pub fn complete(&mut self) {
self.progress.send_modify(|p| p.complete());
self.progress.send_modify(|p| p.set_complete());
self.update_overall();
}
pub fn writer<W>(self, writer: W) -> ProgressTrackerWriter<W> {
@@ -501,7 +542,7 @@ impl PhasedProgressBar {
);
}
}
progress.overall.update_bar(&self.overall, false);
progress.overall.update_bar(&self.overall);
for (name, bar) in self.phases.iter() {
if let Some(progress) = progress.phases.iter().find_map(|p| {
if &p.name == name {
@@ -510,7 +551,7 @@ impl PhasedProgressBar {
None
}
}) {
progress.update_bar(bar, true);
progress.update_bar(bar);
}
}
}

View File

@@ -47,7 +47,7 @@ pub fn admin_api<C: Context>() -> ParentHandler<C> {
from_fn_async(list_admins)
.with_metadata("admin", Value::Bool(true))
.with_display_serializable()
.with_custom_display_fn(|handle, result| Ok(display_signers(handle.params, result)))
.with_custom_display_fn(|handle, result| display_signers(handle.params, result))
.with_about("List admin signers")
.with_call_remote::<CliContext>(),
)
@@ -60,7 +60,7 @@ fn signers_api<C: Context>() -> ParentHandler<C> {
from_fn_async(list_signers)
.with_metadata("admin", Value::Bool(true))
.with_display_serializable()
.with_custom_display_fn(|handle, result| Ok(display_signers(handle.params, result)))
.with_custom_display_fn(|handle, result| display_signers(handle.params, result))
.with_about("List signers")
.with_call_remote::<CliContext>(),
)
@@ -133,7 +133,10 @@ pub async fn list_signers(ctx: RegistryContext) -> Result<BTreeMap<Guid, SignerI
ctx.db.peek().await.into_index().into_signers().de()
}
pub fn display_signers<T>(params: WithIoFormat<T>, signers: BTreeMap<Guid, SignerInfo>) {
pub fn display_signers<T>(
params: WithIoFormat<T>,
signers: BTreeMap<Guid, SignerInfo>,
) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -155,7 +158,8 @@ pub fn display_signers<T>(params: WithIoFormat<T>, signers: BTreeMap<Guid, Signe
&info.keys.into_iter().join("\n"),
]);
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
}
pub async fn add_signer(ctx: RegistryContext, signer: SignerInfo) -> Result<Guid, Error> {

View File

@@ -14,7 +14,7 @@ use url::Url;
use crate::context::CliContext;
use crate::prelude::*;
use crate::progress::FullProgressTracker;
use crate::progress::{FullProgressTracker, ProgressUnits};
use crate::registry::asset::RegistryAsset;
use crate::registry::context::RegistryContext;
use crate::registry::os::index::OsVersionInfo;
@@ -246,6 +246,7 @@ pub async fn cli_add_asset(
if let Some(size) = src.size().await {
verify_phase.set_total(size);
}
verify_phase.set_units(Some(ProgressUnits::Bytes));
let mut writer = verify_phase.writer(VerifyingWriter::new(
tokio::io::sink(),
Some((blake3::Hash::from_bytes(*commitment.hash), commitment.size)),

View File

@@ -13,7 +13,7 @@ use ts_rs::TS;
use crate::context::CliContext;
use crate::prelude::*;
use crate::progress::FullProgressTracker;
use crate::progress::{FullProgressTracker, ProgressUnits};
use crate::registry::asset::RegistryAsset;
use crate::registry::context::RegistryContext;
use crate::registry::os::index::OsVersionInfo;
@@ -167,6 +167,7 @@ async fn cli_get_os_asset(
let mut download_phase =
progress.add_phase(InternedString::intern("Downloading File"), Some(100));
download_phase.set_total(res.commitment.size);
download_phase.set_units(Some(ProgressUnits::Bytes));
let reverify_phase = if reverify {
Some(progress.add_phase(InternedString::intern("Reverifying File"), Some(10)))
} else {

View File

@@ -49,7 +49,7 @@ pub fn version_api<C: Context>() -> ParentHandler<C> {
.with_metadata("get_device_info", Value::Bool(true))
.with_display_serializable()
.with_custom_display_fn(|handle, result| {
Ok(display_version_info(handle.params, result))
display_version_info(handle.params, result)
})
.with_about("Get OS versions and related version info")
.with_call_remote::<CliContext>(),
@@ -197,7 +197,10 @@ pub async fn get_version(
.collect()
}
pub fn display_version_info<T>(params: WithIoFormat<T>, info: BTreeMap<Version, OsVersionInfo>) {
pub fn display_version_info<T>(
params: WithIoFormat<T>,
info: BTreeMap<Version, OsVersionInfo>,
) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -223,5 +226,6 @@ pub fn display_version_info<T>(params: WithIoFormat<T>, info: BTreeMap<Version,
&info.squashfs.keys().into_iter().join(", "),
]);
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
}

View File

@@ -36,7 +36,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
"list",
from_fn_async(list_version_signers)
.with_display_serializable()
.with_custom_display_fn(|handle, result| Ok(display_signers(handle.params, result)))
.with_custom_display_fn(|handle, result| display_signers(handle.params, result))
.with_about("List version signers and related signer info")
.with_call_remote::<CliContext>(),
)

View File

@@ -12,7 +12,7 @@ use url::Url;
use crate::context::CliContext;
use crate::prelude::*;
use crate::progress::{FullProgressTracker, ProgressTrackerWriter};
use crate::progress::{FullProgressTracker, ProgressTrackerWriter, ProgressUnits};
use crate::registry::context::RegistryContext;
use crate::registry::package::index::PackageVersionInfo;
use crate::registry::signer::commitment::merkle_archive::MerkleArchiveCommitment;
@@ -135,6 +135,7 @@ pub async fn cli_add_package(
if let Some(len) = len {
verify_phase.set_total(len);
}
verify_phase.set_units(Some(ProgressUnits::Bytes));
let mut verify_writer = ProgressTrackerWriter::new(tokio::io::sink(), verify_phase);
src.serialize(&mut TrackingIO::new(0, &mut verify_writer), true)
.await?;

View File

@@ -52,7 +52,7 @@ pub fn category_api<C: Context>() -> ParentHandler<C> {
from_fn_async(list_categories)
.with_display_serializable()
.with_custom_display_fn(|params, categories| {
Ok(display_categories(params.params, categories))
display_categories(params.params, categories)
})
.with_call_remote::<CliContext>(),
)
@@ -182,7 +182,7 @@ pub async fn list_categories(
pub fn display_categories<T>(
params: WithIoFormat<T>,
categories: BTreeMap<InternedString, Category>,
) {
) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -197,5 +197,6 @@ pub fn display_categories<T>(
for (id, info) in categories {
table.add_row(row![&*id, &info.name]);
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
}

View File

@@ -36,7 +36,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
"list",
from_fn_async(list_package_signers)
.with_display_serializable()
.with_custom_display_fn(|handle, result| Ok(display_signers(handle.params, result)))
.with_custom_display_fn(|handle, result| display_signers(handle.params, result))
.with_about("List package signers and related signer info")
.with_call_remote::<CliContext>(),
)

View File

@@ -4,7 +4,7 @@ use std::str::FromStr;
use std::sync::Arc;
use exver::{ExtendedVersion, VersionRange};
use models::{Id, ImageId, VolumeId};
use models::{ImageId, VolumeId};
use tokio::io::{AsyncRead, AsyncSeek, AsyncWriteExt};
use tokio::process::Command;
@@ -259,6 +259,7 @@ impl TryFrom<ManifestV1> for Manifest {
},
git_hash: value.git_hash,
os_version: value.eos_version,
sdk_version: None,
})
}
}

View File

@@ -66,6 +66,8 @@ pub struct Manifest {
#[serde(default = "current_version")]
#[ts(type = "string")]
pub os_version: Version,
#[ts(type = "string | null")]
pub sdk_version: Option<Version>,
}
impl Manifest {
pub fn validate_for<'a, T: Clone>(

View File

@@ -31,7 +31,7 @@ pub fn action_api<C: Context>() -> ParentHandler<C> {
"run",
from_fn_async(run_action)
.with_display_serializable()
.with_custom_display_fn(|args, res| Ok(display_action_result(args.params, res)))
.with_custom_display_fn(|args, res| display_action_result(args.params, res))
.with_call_remote::<ContainerCliContext>(),
)
.subcommand("create-task", from_fn_async(create_task).no_cli())

View File

@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
use std::ffi::{c_int, OsStr, OsString};
use std::fs::File;
use std::io::{IsTerminal, Read};
use std::os::unix::process::CommandExt;
use std::os::unix::process::{CommandExt, ExitStatusExt};
use std::path::{Path, PathBuf};
use std::process::{Command as StdCommand, Stdio};
use std::sync::Arc;
@@ -330,7 +330,7 @@ pub fn launch(
if let Some(code) = exit.code() {
drop(raw);
std::process::exit(code);
} else if exit.success() {
} else if exit.success() || exit.signal() == Some(15) {
Ok(())
} else {
Err(Error::new(
@@ -380,7 +380,7 @@ pub fn launch(
nix::mount::umount(&chroot.join("proc"))
.with_ctx(|_| (ErrorKind::Filesystem, "umount procfs"))?;
std::process::exit(code);
} else if exit.success() {
} else if exit.success() || exit.signal() == Some(15) {
Ok(())
} else {
Err(Error::new(

View File

@@ -48,6 +48,7 @@ use crate::s9pk::S9pk;
use crate::service::action::update_tasks;
use crate::service::rpc::{ExitParams, InitKind};
use crate::service::service_map::InstallProgressHandles;
use crate::service::uninstall::cleanup;
use crate::util::actor::concurrent::ConcurrentActor;
use crate::util::io::{create_file, AsyncReadStream, TermSize};
use crate::util::net::WebSocketExt;
@@ -111,7 +112,6 @@ impl std::fmt::Display for MiB {
#[derive(Clone, Debug, Serialize, Deserialize, Default, TS)]
pub struct ServiceStats {
pub container_id: Arc<ContainerId>,
pub package_id: PackageId,
pub memory_usage: MiB,
pub memory_limit: MiB,
}
@@ -307,7 +307,7 @@ impl Service {
}
}
}
// TODO: delete s9pk?
cleanup(ctx, id, false).await.log_err();
ctx.db
.mutate(|v| v.as_public_mut().as_package_data_mut().remove(id))
.await
@@ -615,7 +615,6 @@ impl Service {
.fold((0, 0), |acc, (total, used)| (acc.0 + total, acc.1 + used));
Ok(ServiceStats {
container_id: lxc_container.guid.clone(),
package_id: self.seed.id.clone(),
memory_limit: MiB::from_MiB(total),
memory_usage: MiB::from_MiB(used),
})
@@ -735,6 +734,7 @@ pub struct AttachParams {
pub command: Vec<OsString>,
pub tty: bool,
pub stderr_tty: bool,
pub pty_size: Option<TermSize>,
#[ts(skip)]
#[serde(rename = "__auth_session")]
session: Option<InternedString>,
@@ -752,6 +752,7 @@ pub async fn attach(
command,
tty,
stderr_tty,
pty_size,
session,
subcontainer,
image_id,
@@ -862,6 +863,7 @@ pub async fn attach(
command: Vec<OsString>,
tty: bool,
stderr_tty: bool,
pty_size: Option<TermSize>,
image_id: ImageId,
workdir: Option<String>,
root_command: &RootCommand,
@@ -898,6 +900,10 @@ pub async fn attach(
cmd.arg("--force-stderr-tty");
}
if let Some(pty_size) = pty_size {
cmd.arg(format!("--pty-size={pty_size}"));
}
cmd.arg(&root_path).arg("--");
if command.is_empty() {
@@ -1040,6 +1046,7 @@ pub async fn attach(
command,
tty,
stderr_tty,
pty_size,
image_id,
workdir,
&root_command,

View File

@@ -22,7 +22,9 @@ use crate::disk::mount::guard::GenericMountGuard;
use crate::install::PKG_ARCHIVE_DIR;
use crate::notifications::{notify, NotificationLevel};
use crate::prelude::*;
use crate::progress::{FullProgressTracker, PhaseProgressTrackerHandle, ProgressTrackerWriter};
use crate::progress::{
FullProgressTracker, PhaseProgressTrackerHandle, ProgressTrackerWriter, ProgressUnits,
};
use crate::rpc_continuations::Guid;
use crate::s9pk::manifest::PackageId;
use crate::s9pk::merkle_archive::source::FileSource;
@@ -72,6 +74,7 @@ impl ServiceMap {
progress.start();
let ids = ctx.db.peek().await.as_public().as_package_data().keys()?;
progress.set_total(ids.len() as u64);
progress.set_units(Some(ProgressUnits::Steps));
let mut jobs = FuturesUnordered::new();
for id in &ids {
jobs.push(self.load(ctx, id, LoadDisposition::Retry));

View File

@@ -34,7 +34,7 @@ use crate::disk::REPAIR_DISK_PATH;
use crate::init::{init, InitPhases, InitResult};
use crate::net::ssl::root_ca_start_time;
use crate::prelude::*;
use crate::progress::{FullProgress, PhaseProgressTrackerHandle};
use crate::progress::{FullProgress, PhaseProgressTrackerHandle, ProgressUnits};
use crate::rpc_continuations::Guid;
use crate::system::sync_kiosk;
use crate::util::crypto::EncryptedWire;
@@ -547,6 +547,7 @@ async fn migrate(
let mut restore_phase = restore_phase.or_not_found("restore progress")?;
restore_phase.start();
restore_phase.set_units(Some(ProgressUnits::Bytes));
let _ = crate::disk::main::import(
&old_guid,
"/media/startos/migrate",

View File

@@ -108,7 +108,7 @@ pub fn ssh<C: Context>() -> ParentHandler<C> {
from_fn_async(list)
.with_display_serializable()
.with_custom_display_fn(|handle, result| {
Ok(display_all_ssh_keys(handle.params, result))
display_all_ssh_keys(handle.params, result)
})
.with_about("List ssh keys")
.with_call_remote::<CliContext>(),
@@ -177,7 +177,10 @@ pub async fn remove(
sync_pubkeys(&keys, SSH_DIR).await
}
fn display_all_ssh_keys(params: WithIoFormat<Empty>, result: Vec<SshKeyResponse>) {
fn display_all_ssh_keys(
params: WithIoFormat<Empty>,
result: Vec<SshKeyResponse>,
) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -200,7 +203,9 @@ fn display_all_ssh_keys(params: WithIoFormat<Empty>, result: Vec<SshKeyResponse>
];
table.add_row(row);
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
}
#[instrument(skip_all)]

View File

@@ -46,7 +46,7 @@ pub fn experimental<C: Context>() -> ParentHandler<C> {
from_fn_async(governor)
.with_display_serializable()
.with_custom_display_fn(|handle, result| {
Ok(display_governor_info(handle.params, result))
display_governor_info(handle.params, result)
})
.with_about("Show current and available CPU governors")
.with_call_remote::<CliContext>(),
@@ -125,7 +125,10 @@ pub struct GovernorInfo {
available: BTreeSet<Governor>,
}
fn display_governor_info(params: WithIoFormat<GovernorParams>, result: GovernorInfo) {
fn display_governor_info(
params: WithIoFormat<GovernorParams>,
result: GovernorInfo,
) -> Result<(), Error> {
use prettytable::*;
if let Some(format) = params.format {
@@ -141,7 +144,8 @@ fn display_governor_info(params: WithIoFormat<GovernorParams>, result: GovernorI
table.add_row(row![entry]);
}
}
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
}
#[derive(Deserialize, Serialize, Parser, TS)]
@@ -191,7 +195,7 @@ pub struct TimeInfo {
uptime: u64,
}
pub fn display_time(params: WithIoFormat<Empty>, arg: TimeInfo) {
pub fn display_time(params: WithIoFormat<Empty>, arg: TimeInfo) -> Result<(), Error> {
use std::fmt::Write;
use prettytable::*;
@@ -230,7 +234,8 @@ pub fn display_time(params: WithIoFormat<Empty>, arg: TimeInfo) {
let mut table = Table::new();
table.add_row(row![bc -> "NOW", &arg.now]);
table.add_row(row![bc -> "UPTIME", &uptime_string]);
table.print_tty(false).unwrap();
table.print_tty(false)?;
Ok(())
}
pub async fn time(ctx: RpcContext, _: Empty) -> Result<TimeInfo, Error> {

View File

@@ -26,7 +26,9 @@ use crate::disk::mount::filesystem::MountType;
use crate::disk::mount::guard::{GenericMountGuard, MountGuard, TmpMountGuard};
use crate::notifications::{notify, NotificationLevel};
use crate::prelude::*;
use crate::progress::{FullProgressTracker, PhaseProgressTrackerHandle, PhasedProgressBar};
use crate::progress::{
FullProgressTracker, PhaseProgressTrackerHandle, PhasedProgressBar, ProgressUnits,
};
use crate::registry::asset::RegistryAsset;
use crate::registry::context::{RegistryContext, RegistryUrlParams};
use crate::registry::os::index::OsVersionInfo;
@@ -195,9 +197,9 @@ pub async fn cli_update_system(
}
if let Some(mut prev) = prev {
for phase in &mut prev.phases {
phase.progress.complete();
phase.progress.set_complete();
}
prev.overall.complete();
prev.overall.set_complete();
progress.update(&prev);
}
} else {
@@ -265,6 +267,7 @@ async fn maybe_do_update(
let prune_phase = progress.add_phase("Pruning Old OS Images".into(), Some(2));
let mut download_phase = progress.add_phase("Downloading File".into(), Some(100));
download_phase.set_total(asset.commitment.size);
download_phase.set_units(Some(ProgressUnits::Bytes));
let reverify_phase = progress.add_phase("Reverifying File".into(), Some(10));
let sync_boot_phase = progress.add_phase("Syncing Boot Files".into(), Some(1));
let finalize_phase = progress.add_phase("Finalizing Update".into(), Some(1));
@@ -399,6 +402,9 @@ async fn do_update(
.arg(asset.commitment.size.to_string())
.invoke(ErrorKind::Filesystem)
.await?;
Command::new("/usr/lib/startos/scripts/prune-boot")
.invoke(ErrorKind::Filesystem)
.await?;
prune_phase.complete();
download_phase.start();

View File

@@ -18,7 +18,7 @@ use tokio::sync::watch;
use crate::context::RpcContext;
use crate::prelude::*;
use crate::progress::PhaseProgressTrackerHandle;
use crate::progress::{PhaseProgressTrackerHandle, ProgressUnits};
use crate::rpc_continuations::{Guid, RpcContinuation};
use crate::s9pk::merkle_archive::source::multi_cursor_file::{FileCursor, MultiCursorFile};
use crate::s9pk::merkle_archive::source::ArchiveSource;
@@ -176,7 +176,10 @@ pub struct UploadingFile {
progress: watch::Receiver<Progress>,
}
impl UploadingFile {
pub async fn new(progress: PhaseProgressTrackerHandle) -> Result<(UploadHandle, Self), Error> {
pub async fn new(
mut progress: PhaseProgressTrackerHandle,
) -> Result<(UploadHandle, Self), Error> {
progress.set_units(Some(ProgressUnits::Bytes));
let progress = watch::channel(Progress {
tracker: progress,
expected_size: None,

View File

@@ -26,10 +26,10 @@ use tokio::io::{
use tokio::net::TcpStream;
use tokio::sync::{Notify, OwnedMutexGuard};
use tokio::time::{Instant, Sleep};
use ts_rs::TS;
use crate::prelude::*;
use crate::util::sync::SyncMutex;
use crate::{CAP_1_KiB, CAP_1_MiB};
pub trait AsyncReadSeek: AsyncRead + AsyncSeek {}
impl<T: AsyncRead + AsyncSeek> AsyncReadSeek for T {}
@@ -1426,7 +1426,7 @@ impl<T: std::io::Read> std::io::Read for SharedIO<T> {
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct TermSize {
pub size: (u16, u16),
pub pixels: Option<(u16, u16)>,
@@ -1464,6 +1464,15 @@ impl FromStr for TermSize {
.ok_or_else(|| Error::new(eyre!("invalid pty size"), ErrorKind::ParseNumber))
}
}
impl std::fmt::Display for TermSize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.size.0, self.size.1)?;
if let Some(pixels) = self.pixels {
write!(f, ":{}:{}", pixels.0, pixels.1)?;
}
Ok(())
}
}
impl ValueParserFactory for TermSize {
type Parser = FromStrParser<Self>;
fn value_parser() -> Self::Parser {

View File

@@ -398,13 +398,12 @@ impl IoFormat {
}
}
pub fn display_serializable<T: Serialize>(format: IoFormat, result: T) {
format
.to_writer(std::io::stdout(), &result)
.expect("Error serializing result to stdout");
pub fn display_serializable<T: Serialize>(format: IoFormat, result: T) -> Result<(), Error> {
format.to_writer(std::io::stdout(), &result)?;
if format == IoFormat::JsonPretty {
println!()
}
Ok(())
}
#[derive(Deserialize, Serialize)]
@@ -523,13 +522,14 @@ impl<T: HandlerFor<C>, C: Context> HandlerFor<C> for DisplaySerializable<T> {
impl<T: HandlerTypes, C: Context> PrintCliResult<C> for DisplaySerializable<T>
where
T::Ok: Serialize,
Self::Err: From<Error>,
{
fn print(
&self,
HandlerArgs { params, .. }: HandlerArgsFor<C, Self>,
result: Self::Ok,
) -> Result<(), Self::Err> {
display_serializable(params.format.unwrap_or_default(), result);
display_serializable(params.format.unwrap_or_default(), result)?;
Ok(())
}
}

View File

@@ -46,8 +46,9 @@ mod v0_4_0_alpha_3;
mod v0_4_0_alpha_4;
mod v0_4_0_alpha_5;
mod v0_4_0_alpha_6;
mod v0_4_0_alpha_7;
pub type Current = v0_4_0_alpha_6::Version; // VERSION_BUMP
pub type Current = v0_4_0_alpha_7::Version; // VERSION_BUMP
impl Current {
#[instrument(skip(self, db))]
@@ -157,7 +158,8 @@ enum Version {
V0_4_0_alpha_3(Wrapper<v0_4_0_alpha_3::Version>),
V0_4_0_alpha_4(Wrapper<v0_4_0_alpha_4::Version>),
V0_4_0_alpha_5(Wrapper<v0_4_0_alpha_5::Version>),
V0_4_0_alpha_6(Wrapper<v0_4_0_alpha_6::Version>), // VERSION_BUMP
V0_4_0_alpha_6(Wrapper<v0_4_0_alpha_6::Version>),
V0_4_0_alpha_7(Wrapper<v0_4_0_alpha_7::Version>), // VERSION_BUMP
Other(exver::Version),
}
@@ -206,7 +208,8 @@ impl Version {
Self::V0_4_0_alpha_3(v) => DynVersion(Box::new(v.0)),
Self::V0_4_0_alpha_4(v) => DynVersion(Box::new(v.0)),
Self::V0_4_0_alpha_5(v) => DynVersion(Box::new(v.0)),
Self::V0_4_0_alpha_6(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
Self::V0_4_0_alpha_6(v) => DynVersion(Box::new(v.0)),
Self::V0_4_0_alpha_7(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
Self::Other(v) => {
return Err(Error::new(
eyre!("unknown version {v}"),
@@ -247,7 +250,8 @@ impl Version {
Version::V0_4_0_alpha_3(Wrapper(x)) => x.semver(),
Version::V0_4_0_alpha_4(Wrapper(x)) => x.semver(),
Version::V0_4_0_alpha_5(Wrapper(x)) => x.semver(),
Version::V0_4_0_alpha_6(Wrapper(x)) => x.semver(), // VERSION_BUMP
Version::V0_4_0_alpha_6(Wrapper(x)) => x.semver(),
Version::V0_4_0_alpha_7(Wrapper(x)) => x.semver(), // VERSION_BUMP
Version::Other(x) => x.clone(),
}
}

View File

@@ -0,0 +1,37 @@
use exver::{PreReleaseSegment, VersionRange};
use super::v0_3_5::V0_3_0_COMPAT;
use super::{v0_4_0_alpha_6, VersionT};
use crate::prelude::*;
lazy_static::lazy_static! {
static ref V0_4_0_alpha_7: exver::Version = exver::Version::new(
[0, 4, 0],
[PreReleaseSegment::String("alpha".into()), 7.into()]
);
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Version;
impl VersionT for Version {
type Previous = v0_4_0_alpha_6::Version;
type PreUpRes = ();
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
Ok(())
}
fn semver(self) -> exver::Version {
V0_4_0_alpha_7.clone()
}
fn compat(self) -> &'static VersionRange {
&V0_3_0_COMPAT
}
#[instrument]
fn up(self, _db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> {
Ok(())
}
fn down(self, _db: &mut Value) -> Result<(), Error> {
Ok(())
}
}