mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
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:
2
core/Cargo.lock
generated
2
core/Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)| {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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))]
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
})
|
||||
|
||||
@@ -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(())
|
||||
})
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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))]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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>(),
|
||||
)
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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>(),
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
37
core/startos/src/version/v0_4_0_alpha_7.rs
Normal file
37
core/startos/src/version/v0_4_0_alpha_7.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user