From 9e63f3f7c619df338b341888cf8ea62e2f22107f Mon Sep 17 00:00:00 2001 From: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Date: Thu, 20 Mar 2025 15:54:05 -0600 Subject: [PATCH 1/5] add callback for getContainerIp (#2851) * add callback for getContainerIp * register callback before retrieving info * version bump; only use backports for linux --- .../src/Adapters/EffectCreator.ts | 4 +- core/Cargo.lock | 2 +- core/startos/Cargo.toml | 2 +- core/startos/src/auth.rs | 2 + core/startos/src/backup/backup_bulk.rs | 13 +- core/startos/src/backup/target/cifs.rs | 9 +- core/startos/src/context/rpc.rs | 10 +- core/startos/src/db/mod.rs | 4 +- core/startos/src/db/prelude.rs | 2 +- core/startos/src/init.rs | 9 +- core/startos/src/install/mod.rs | 3 +- core/startos/src/middleware/auth.rs | 6 +- core/startos/src/net/acme.rs | 12 +- core/startos/src/net/host/address.rs | 11 +- core/startos/src/net/host/binding.rs | 3 +- core/startos/src/net/net_controller.rs | 12 +- core/startos/src/net/network_interface.rs | 3 +- core/startos/src/net/tor.rs | 4 +- core/startos/src/net/vhost.rs | 3 +- core/startos/src/net/wifi.rs | 10 +- core/startos/src/notifications.rs | 4 + core/startos/src/progress.rs | 3 +- core/startos/src/registry/admin.rs | 7 +- core/startos/src/registry/db.rs | 4 +- core/startos/src/registry/info.rs | 2 + core/startos/src/registry/os/asset/add.rs | 3 +- core/startos/src/registry/os/asset/sign.rs | 1 + core/startos/src/registry/os/version/mod.rs | 2 + .../startos/src/registry/os/version/signer.rs | 2 + core/startos/src/registry/package/add.rs | 1 + core/startos/src/registry/package/category.rs | 6 +- core/startos/src/registry/package/signer.rs | 2 + core/startos/src/s9pk/v2/pack.rs | 38 +++--- core/startos/src/service/action.rs | 3 +- core/startos/src/service/effects/action.rs | 12 +- core/startos/src/service/effects/callbacks.rs | 32 ++++- core/startos/src/service/effects/control.rs | 17 +-- .../startos/src/service/effects/dependency.rs | 1 + core/startos/src/service/effects/health.rs | 3 +- core/startos/src/service/effects/mod.rs | 2 - core/startos/src/service/effects/net/host.rs | 18 +-- core/startos/src/service/effects/net/info.rs | 52 +++++++- .../src/service/effects/net/interface.rs | 121 ++++++++++++------ core/startos/src/service/effects/net/ssl.rs | 6 +- core/startos/src/service/effects/store.rs | 14 +- core/startos/src/service/effects/system.rs | 19 +-- core/startos/src/service/mod.rs | 14 +- .../src/service/persistent_container.rs | 10 +- core/startos/src/service/service_actor.rs | 3 +- core/startos/src/service/service_map.rs | 110 ++++++++-------- core/startos/src/setup.rs | 3 +- core/startos/src/shutdown.rs | 6 +- core/startos/src/ssh.rs | 6 +- core/startos/src/system.rs | 12 +- core/startos/src/update/mod.rs | 34 ++--- core/startos/src/version/mod.rs | 21 ++- core/startos/src/version/v0_3_6_alpha_17.rs | 36 ++++++ core/startos/src/version/v0_3_6_alpha_6.rs | 3 +- image-recipe/build.sh | 2 +- patch-db | 2 +- sdk/base/lib/Effects.ts | 5 +- .../lib/osBindings/GetContainerIpParams.ts | 8 ++ sdk/base/lib/osBindings/index.ts | 1 + .../lib/test/startosTypeValidation.test.ts | 3 +- sdk/package/lib/StartSdk.ts | 64 ++++++++- sdk/package/package-lock.json | 4 +- sdk/package/package.json | 2 +- web/package-lock.json | 4 +- web/package.json | 2 +- 69 files changed, 592 insertions(+), 262 deletions(-) create mode 100644 core/startos/src/version/v0_3_6_alpha_17.rs create mode 100644 sdk/base/lib/osBindings/GetContainerIpParams.ts diff --git a/container-runtime/src/Adapters/EffectCreator.ts b/container-runtime/src/Adapters/EffectCreator.ts index ed975f25e..e11cd9d43 100644 --- a/container-runtime/src/Adapters/EffectCreator.ts +++ b/container-runtime/src/Adapters/EffectCreator.ts @@ -193,8 +193,8 @@ export function makeEffects(context: EffectContext): Effects { T.Effects["exposeForDependents"] > }, - getContainerIp(...[]: Parameters) { - return rpcRound("get-container-ip", {}) as ReturnType< + getContainerIp(...[options]: Parameters) { + return rpcRound("get-container-ip", options) as ReturnType< T.Effects["getContainerIp"] > }, diff --git a/core/Cargo.lock b/core/Cargo.lock index a68b20515..7ec339ded 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -5952,7 +5952,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "start-os" -version = "0.3.6-alpha.16" +version = "0.3.6-alpha.17" dependencies = [ "aes 0.7.5", "async-acme", diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index a04df93fd..e0bace2af 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -14,7 +14,7 @@ keywords = [ name = "start-os" readme = "README.md" repository = "https://github.com/Start9Labs/start-os" -version = "0.3.6-alpha.16" # VERSION_BUMP +version = "0.3.6-alpha.17" # VERSION_BUMP license = "MIT" [lib] diff --git a/core/startos/src/auth.rs b/core/startos/src/auth.rs index 9085709ab..2bc85b7db 100644 --- a/core/startos/src/auth.rs +++ b/core/startos/src/auth.rs @@ -247,6 +247,7 @@ pub async fn login_impl( Ok(hash_token.to_login_res()) }) .await + .result } } @@ -476,6 +477,7 @@ pub async fn reset_password_impl( Ok(()) }) .await + .result } #[instrument(skip_all)] diff --git a/core/startos/src/backup/backup_bulk.rs b/core/startos/src/backup/backup_bulk.rs index 136595b67..28aee82c1 100644 --- a/core/startos/src/backup/backup_bulk.rs +++ b/core/startos/src/backup/backup_bulk.rs @@ -59,7 +59,8 @@ impl BackupStatusGuard { .as_backup_progress_mut() .ser(&None) }) - .await?; + .await + .result?; } if let Some(db) = self.0.take() { match result { @@ -124,7 +125,8 @@ impl BackupStatusGuard { }) .await } - }?; + } + .result?; } Ok(()) } @@ -141,6 +143,7 @@ impl Drop for BackupStatusGuard { .ser(&None) }) .await + .result .log_err() }); } @@ -187,7 +190,8 @@ pub async fn backup_all( db.as_public().as_server_info().as_id().de()?, )) }) - .await?, + .await + .result?, BackupStatusGuard::new(ctx.db.clone()), ); @@ -348,7 +352,8 @@ async fn perform_backup( .as_last_backup_mut() .ser(&Some(timestamp)) }) - .await?; + .await + .result?; Ok(backup_report) } diff --git a/core/startos/src/backup/target/cifs.rs b/core/startos/src/backup/target/cifs.rs index 71cbe267e..63e18d0d8 100644 --- a/core/startos/src/backup/target/cifs.rs +++ b/core/startos/src/backup/target/cifs.rs @@ -112,7 +112,8 @@ pub async fn add( db.as_private_mut().as_cifs_mut().insert(&id, &cifs)?; Ok(id) }) - .await?; + .await + .result?; Ok(KeyVal { key: BackupTargetId::Cifs { id }, value: BackupTarget::Cifs(CifsBackupTarget { @@ -176,7 +177,8 @@ pub async fn update( })? .ser(&cifs) }) - .await?; + .await + .result?; Ok(KeyVal { key: BackupTargetId::Cifs { id }, value: BackupTarget::Cifs(CifsBackupTarget { @@ -207,7 +209,8 @@ pub async fn remove(ctx: RpcContext, RemoveParams { id }: RemoveParams) -> Resul }; ctx.db .mutate(|db| db.as_private_mut().as_cifs_mut().remove(&id)) - .await?; + .await + .result?; Ok(()) } diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index 96ca3ca63..8bd138b86 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -202,7 +202,8 @@ impl RpcContext { .ser(&true) }) .await - .unwrap() + .result + .log_err(); }) .into(), ) @@ -330,7 +331,8 @@ impl RpcContext { } Ok(()) }) - .await?; + .await + .result?; let db = self.db.clone(); self.add_cron(async move { loop { @@ -355,6 +357,7 @@ impl RpcContext { Ok(()) }) .await + .result { tracing::error!("Error in session cleanup cron: {e}"); tracing::debug!("{e:?}"); @@ -424,7 +427,8 @@ impl RpcContext { } Ok(()) }) - .await?; + .await + .result?; check_requested_actions.complete(); Ok(()) diff --git a/core/startos/src/db/mod.rs b/core/startos/src/db/mod.rs index 135153935..6575b401b 100644 --- a/core/startos/src/db/mod.rs +++ b/core/startos/src/db/mod.rs @@ -290,7 +290,8 @@ async fn cli_apply( }; Ok::<_, Error>((value, ())) }) - .await?; + .await + .result?; } else { let method = parent_method.into_iter().chain(method).join("."); context @@ -328,6 +329,7 @@ pub async fn apply(ctx: RpcContext, ApplyParams { expr }: ApplyParams) -> Result ) }) .await + .result } pub fn put() -> ParentHandler { diff --git a/core/startos/src/db/prelude.rs b/core/startos/src/db/prelude.rs index 419b356ef..136f2dd77 100644 --- a/core/startos/src/db/prelude.rs +++ b/core/startos/src/db/prelude.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use chrono::{DateTime, Utc}; pub use imbl_value::Value; use patch_db::value::InternedString; -pub use patch_db::{HasModel, PatchDb}; +pub use patch_db::{HasModel, MutateResult, PatchDb}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; diff --git a/core/startos/src/init.rs b/core/startos/src/init.rs index 63642dfb3..ef2455a29 100644 --- a/core/startos/src/init.rs +++ b/core/startos/src/init.rs @@ -419,7 +419,8 @@ pub async fn init( wifi.as_interface_mut().ser(&wifi_interface)?; wifi.de() }) - .await?; + .await + .result?; crate::net::wifi::synchronize_network_manager(MAIN_DATA, &wifi).await?; load_wifi.complete(); tracing::info!("Synchronized WiFi"); @@ -522,7 +523,8 @@ pub async fn init( server_info.as_status_info_mut().ser(&status_info)?; Ok(()) }) - .await?; + .await + .result?; tracing::info!("Updated server info"); update_server_info.complete(); @@ -540,7 +542,8 @@ pub async fn init( let model = d.de()?; d.ser(&model) }) - .await?; + .await + .result?; tracing::info!("Validated database"); validate_db.complete(); diff --git a/core/startos/src/install/mod.rs b/core/startos/src/install/mod.rs index ef0da2916..d7a939271 100644 --- a/core/startos/src/install/mod.rs +++ b/core/startos/src/install/mod.rs @@ -541,7 +541,8 @@ pub async fn uninstall( )), }) }) - .await?; + .await + .result?; let return_id = id.clone(); diff --git a/core/startos/src/middleware/auth.rs b/core/startos/src/middleware/auth.rs index 7c5eaa4c2..a43f22529 100644 --- a/core/startos/src/middleware/auth.rs +++ b/core/startos/src/middleware/auth.rs @@ -65,7 +65,8 @@ impl HasLoggedOutSessions { Ok(()) }) - .await?; + .await + .result?; Ok(HasLoggedOutSessions(())) } } @@ -136,7 +137,8 @@ impl HasValidSession { Ok(()) }) }) - .await?; + .await + .result?; } Ok(Self(SessionType::Session(session_token))) } diff --git a/core/startos/src/net/acme.rs b/core/startos/src/net/acme.rs index 7e2dd23de..2183f001a 100644 --- a/core/startos/src/net/acme.rs +++ b/core/startos/src/net/acme.rs @@ -74,7 +74,8 @@ impl<'a> async_acme::cache::AcmeCache for AcmeCertCache<'a> { .as_accounts_mut() .insert(&contacts, &Pem::new(key)) }) - .await?; + .await + .result?; Ok(()) } @@ -166,7 +167,8 @@ impl<'a> async_acme::cache::AcmeCache for AcmeCertCache<'a> { .upsert(&directory_url, || Ok(BTreeMap::new()))? .insert(&identifiers, &cert) }) - .await?; + .await + .result?; Ok(()) } @@ -259,7 +261,8 @@ pub async fn init( .as_acme_mut() .insert(&provider, &AcmeSettings { contact }) }) - .await?; + .await + .result?; Ok(()) } @@ -280,6 +283,7 @@ pub async fn remove( .as_acme_mut() .remove(&provider) }) - .await?; + .await + .result?; Ok(()) } diff --git a/core/startos/src/net/host/address.rs b/core/startos/src/net/host/address.rs index b75734a13..bd08c70a3 100644 --- a/core/startos/src/net/host/address.rs +++ b/core/startos/src/net/host/address.rs @@ -202,7 +202,7 @@ pub async fn add_domain( )?; check_duplicates(db) }) - .await?; + .await.result?; Kind::sync_host(&ctx, inheritance).await?; Ok(()) @@ -224,7 +224,8 @@ pub async fn remove_domain( .as_domains_mut() .remove(&domain) }) - .await?; + .await + .result?; Kind::sync_host(&ctx, inheritance).await?; Ok(()) @@ -258,7 +259,8 @@ pub async fn add_onion( .mutate(|a| Ok(a.insert(onion)))?; check_duplicates(db) }) - .await?; + .await + .result?; Kind::sync_host(&ctx, inheritance).await?; @@ -285,7 +287,8 @@ pub async fn remove_onion( .as_onions_mut() .mutate(|a| Ok(a.remove(&onion))) }) - .await?; + .await + .result?; Kind::sync_host(&ctx, inheritance).await?; diff --git a/core/startos/src/net/host/binding.rs b/core/startos/src/net/host/binding.rs index 5b726a82d..2d27a38b2 100644 --- a/core/startos/src/net/host/binding.rs +++ b/core/startos/src/net/host/binding.rs @@ -239,6 +239,7 @@ pub async fn set_public( Ok(()) }) }) - .await?; + .await + .result?; Kind::sync_host(&ctx, inheritance).await } diff --git a/core/startos/src/net/net_controller.rs b/core/startos/src/net/net_controller.rs index c3d0e8676..ca687bedb 100644 --- a/core/startos/src/net/net_controller.rs +++ b/core/startos/src/net/net_controller.rs @@ -178,7 +178,8 @@ impl NetServiceData { } Ok(res) }) - .await?; + .await + .result?; let mut errors = ErrorCollection::new(); for (id, host) in hosts.0 { errors.handle(self.update(ctrl, id, host).await); @@ -202,7 +203,8 @@ impl NetServiceData { })?; host.de() }) - .await?; + .await + .result?; self.update(ctrl, HostId::default(), host).await } } @@ -580,7 +582,8 @@ impl NetServiceData { .as_hostname_info_mut() .ser(&hostname_info) }) - .await?; + .await + .result?; Ok(()) } @@ -681,7 +684,8 @@ impl NetService { db.as_private_mut().as_available_ports_mut().ser(&ports)?; Ok(host) }) - .await?; + .await + .result?; data.update(&*ctrl, id, host).await } diff --git a/core/startos/src/net/network_interface.rs b/core/startos/src/net/network_interface.rs index c870629d4..d89745730 100644 --- a/core/startos/src/net/network_interface.rs +++ b/core/startos/src/net/network_interface.rs @@ -666,7 +666,8 @@ impl NetworkInterfaceController { .as_network_interfaces_mut() .ser(info) }) - .await?; + .await + .result?; let ntp: BTreeSet<_> = info .values() diff --git a/core/startos/src/net/tor.rs b/core/startos/src/net/tor.rs index bba50c371..14934d614 100644 --- a/core/startos/src/net/tor.rs +++ b/core/startos/src/net/tor.rs @@ -156,6 +156,7 @@ pub async fn generate_key(ctx: RpcContext) -> Result { .get_onion_address()) }) .await + .result } #[derive(Deserialize, Serialize, Parser)] @@ -175,7 +176,8 @@ pub async fn add_key( .as_onion_mut() .insert_key(&key) }) - .await?; + .await + .result?; Ok(key.public().get_onion_address()) } diff --git a/core/startos/src/net/vhost.rs b/core/startos/src/net/vhost.rs index 6317fa8c9..a20d7dda7 100644 --- a/core/startos/src/net/vhost.rs +++ b/core/startos/src/net/vhost.rs @@ -499,7 +499,8 @@ impl VHostServer { .as_local_certs_mut() .cert_for(&hostnames) }) - .await?; + .await + .result?; let cfg = ServerConfig::builder_with_provider(crypto_provider.clone()) .with_safe_default_protocol_versions() .with_kind(crate::ErrorKind::OpenSsl)? diff --git a/core/startos/src/net/wifi.rs b/core/startos/src/net/wifi.rs index a70fbfc73..28025ec99 100644 --- a/core/startos/src/net/wifi.rs +++ b/core/startos/src/net/wifi.rs @@ -161,7 +161,8 @@ pub async fn add(ctx: RpcContext, AddParams { ssid, password }: AddParams) -> Re Ok(()) }) }) - .await?; + .await + .result?; Ok(()) } #[derive(Deserialize, Serialize, Parser, TS)] @@ -225,7 +226,8 @@ pub async fn connect(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result })?; wifi.as_selected_mut().ser(&Some(ssid)) }) - .await?; + .await + .result?; Ok(()) } @@ -262,7 +264,8 @@ pub async fn remove(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result< wifi.as_selected_mut() .map_mutate(|s| Ok(s.filter(|s| s == &ssid.0))) }) - .await?; + .await + .result?; Ok(()) } #[derive(serde::Serialize, serde::Deserialize)] @@ -739,6 +742,7 @@ impl WpaCli { .ser(&new_country) }) .await + .result } async fn check_active_network(&self, ssid: &Ssid) -> Result, Error> { Ok(self diff --git a/core/startos/src/notifications.rs b/core/startos/src/notifications.rs index 48444020a..b586a98c6 100644 --- a/core/startos/src/notifications.rs +++ b/core/startos/src/notifications.rs @@ -116,6 +116,7 @@ pub async fn list( } }) .await + .result } #[derive(Deserialize, Serialize, Parser, TS)] @@ -133,6 +134,7 @@ pub async fn remove(ctx: RpcContext, DeleteParams { id }: DeleteParams) -> Resul Ok(()) }) .await + .result } #[derive(Deserialize, Serialize, Parser, TS)] #[serde(rename_all = "camelCase")] @@ -156,6 +158,7 @@ pub async fn remove_before( Ok(()) }) .await + .result } #[derive(Deserialize, Serialize, Parser, TS)] @@ -180,6 +183,7 @@ pub async fn create( ctx.db .mutate(|db| notify(db, package, level, title, message, ())) .await + .result } #[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, TS)] diff --git a/core/startos/src/progress.rs b/core/startos/src/progress.rs index cc3257132..38497d6cf 100644 --- a/core/startos/src/progress.rs +++ b/core/startos/src/progress.rs @@ -294,7 +294,8 @@ impl FullProgressTracker { Ok(true) } }) - .await? + .await + .result? { break; } diff --git a/core/startos/src/registry/admin.rs b/core/startos/src/registry/admin.rs index f3cac9f7e..ffc9c3b35 100644 --- a/core/startos/src/registry/admin.rs +++ b/core/startos/src/registry/admin.rs @@ -148,6 +148,7 @@ pub async fn add_signer(ctx: RegistryContext, signer: SignerInfo) -> Result( @@ -279,6 +282,7 @@ pub async fn add_admin( Ok(()) }) .await + .result } #[derive(Debug, Deserialize, Serialize, Parser)] @@ -310,7 +314,8 @@ pub async fn cli_add_admin( db.as_admins_mut().mutate(|a| Ok(a.insert(signer)))?; Ok(()) }) - .await?; + .await + .result?; } else { ctx.call_remote::( &parent_method.into_iter().chain(method).join("."), diff --git a/core/startos/src/registry/db.rs b/core/startos/src/registry/db.rs index 8de9f8743..59a55d9fb 100644 --- a/core/startos/src/registry/db.rs +++ b/core/startos/src/registry/db.rs @@ -136,7 +136,8 @@ async fn cli_apply( (), )) }) - .await?; + .await + .result?; } else { let method = parent_method.into_iter().chain(method).join("."); context @@ -178,4 +179,5 @@ pub async fn apply( ) }) .await + .result } diff --git a/core/startos/src/registry/info.rs b/core/startos/src/registry/info.rs index 402f0891a..10efda2cb 100644 --- a/core/startos/src/registry/info.rs +++ b/core/startos/src/registry/info.rs @@ -79,6 +79,7 @@ pub async fn set_name( ctx.db .mutate(|db| db.as_index_mut().as_name_mut().ser(&Some(name))) .await + .result } #[derive(Debug, Deserialize, Serialize, TS)] @@ -95,6 +96,7 @@ pub async fn set_icon( ctx.db .mutate(|db| db.as_index_mut().as_icon_mut().ser(&Some(icon))) .await + .result } #[derive(Debug, Deserialize, Serialize, Parser, TS)] diff --git a/core/startos/src/registry/os/asset/add.rs b/core/startos/src/registry/os/asset/add.rs index d609063ea..09a6ad164 100644 --- a/core/startos/src/registry/os/asset/add.rs +++ b/core/startos/src/registry/os/asset/add.rs @@ -131,7 +131,8 @@ async fn add_asset( Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) } }) - .await?; + .await + .result?; Ok(()) } diff --git a/core/startos/src/registry/os/asset/sign.rs b/core/startos/src/registry/os/asset/sign.rs index 18b603daf..a2094741b 100644 --- a/core/startos/src/registry/os/asset/sign.rs +++ b/core/startos/src/registry/os/asset/sign.rs @@ -117,6 +117,7 @@ async fn sign_asset( Ok(()) }) .await + .result } pub async fn sign_iso(ctx: RegistryContext, params: SignAssetParams) -> Result<(), Error> { diff --git a/core/startos/src/registry/os/version/mod.rs b/core/startos/src/registry/os/version/mod.rs index 8e4349ed9..12661e4e1 100644 --- a/core/startos/src/registry/os/version/mod.rs +++ b/core/startos/src/registry/os/version/mod.rs @@ -98,6 +98,7 @@ pub async fn add_version( }) }) .await + .result } #[derive(Debug, Deserialize, Serialize, Parser, TS)] @@ -122,6 +123,7 @@ pub async fn remove_version( Ok(()) }) .await + .result } #[derive(Debug, Deserialize, Serialize, Parser, TS)] diff --git a/core/startos/src/registry/os/version/signer.rs b/core/startos/src/registry/os/version/signer.rs index c72bb5ef4..c07e2fa0d 100644 --- a/core/startos/src/registry/os/version/signer.rs +++ b/core/startos/src/registry/os/version/signer.rs @@ -75,6 +75,7 @@ pub async fn add_version_signer( Ok(()) }) .await + .result } pub async fn remove_version_signer( @@ -101,6 +102,7 @@ pub async fn remove_version_signer( Ok(()) }) .await + .result } #[derive(Debug, Deserialize, Serialize, Parser, TS)] diff --git a/core/startos/src/registry/package/add.rs b/core/startos/src/registry/package/add.rs index c52f06ac0..bf3d36fd1 100644 --- a/core/startos/src/registry/package/add.rs +++ b/core/startos/src/registry/package/add.rs @@ -89,6 +89,7 @@ pub async fn add_package( } }) .await + .result } #[derive(Debug, Deserialize, Serialize, Parser)] diff --git a/core/startos/src/registry/package/category.rs b/core/startos/src/registry/package/category.rs index 97b0fb227..7afb8906b 100644 --- a/core/startos/src/registry/package/category.rs +++ b/core/startos/src/registry/package/category.rs @@ -78,7 +78,8 @@ pub async fn add_category( }, ) }) - .await?; + .await + .result?; Ok(()) } @@ -102,7 +103,8 @@ pub async fn remove_category( .as_categories_mut() .remove(&id) }) - .await?; + .await + .result?; Ok(()) } diff --git a/core/startos/src/registry/package/signer.rs b/core/startos/src/registry/package/signer.rs index 56bfc9b1c..7760bad3f 100644 --- a/core/startos/src/registry/package/signer.rs +++ b/core/startos/src/registry/package/signer.rs @@ -74,6 +74,7 @@ pub async fn add_package_signer( Ok(()) }) .await + .result } pub async fn remove_package_signer( @@ -100,6 +101,7 @@ pub async fn remove_package_signer( Ok(()) }) .await + .result } #[derive(Debug, Deserialize, Serialize, Parser, TS)] diff --git a/core/startos/src/s9pk/v2/pack.rs b/core/startos/src/s9pk/v2/pack.rs index f16649ece..aa661fc95 100644 --- a/core/startos/src/s9pk/v2/pack.rs +++ b/core/startos/src/s9pk/v2/pack.rs @@ -139,18 +139,20 @@ impl From for DynFileSource { #[derive(Deserialize, Serialize, Parser)] pub struct PackParams { pub path: Option, - #[arg(short = 'o', long = "output")] + #[arg(short, long)] pub output: Option, - #[arg(long = "javascript")] + #[arg(long)] pub javascript: Option, - #[arg(long = "icon")] + #[arg(long)] pub icon: Option, - #[arg(long = "license")] + #[arg(long)] pub license: Option, - #[arg(long = "instructions")] + #[arg(long)] pub instructions: Option, - #[arg(long = "assets")] + #[arg(long, conflicts_with = "no-assets")] pub assets: Option, + #[arg(long, conflicts_with = "assets")] + pub no_assets: bool, } impl PackParams { fn path(&self) -> &Path { @@ -693,14 +695,16 @@ pub async fn pack(ctx: CliContext, params: PackParams) -> Result<(), Error> { ) .await?; - let assets_dir = params.assets(); - s9pk.as_archive_mut().contents_mut().insert_path( - "assets.squashfs", - Entry::file(TmpSource::new( - tmp_dir.clone(), - PackSource::Squashfs(Arc::new(SqfsDir::new(assets_dir, tmp_dir.clone()))), - )), - )?; + if !params.no_assets { + let assets_dir = params.assets(); + s9pk.as_archive_mut().contents_mut().insert_path( + "assets.squashfs", + Entry::file(TmpSource::new( + tmp_dir.clone(), + PackSource::Squashfs(Arc::new(SqfsDir::new(assets_dir, tmp_dir.clone()))), + )), + )?; + } s9pk.load_images(tmp_dir.clone()).await?; @@ -810,8 +814,10 @@ pub async fn list_ingredients(_: CliContext, params: PackParams) -> Result for ServiceActor { } Ok(()) }) - .await?; + .await + .result?; Ok(result) } } diff --git a/core/startos/src/service/effects/action.rs b/core/startos/src/service/effects/action.rs index 5e3605679..cbab573c2 100644 --- a/core/startos/src/service/effects/action.rs +++ b/core/startos/src/service/effects/action.rs @@ -71,7 +71,8 @@ pub async fn export_action( value.insert(id, metadata); model.ser(&value) }) - .await?; + .await + .result?; Ok(()) } @@ -102,7 +103,8 @@ async fn clear_actions( .as_actions_mut() .mutate(|a| Ok(a.retain(|e, _| except.contains(e)))) }) - .await?; + .await + .result?; Ok(()) } @@ -271,7 +273,8 @@ async fn request_action( .as_requested_actions_mut() .insert(&replay_id, &ActionRequestEntry { active, request }) }) - .await?; + .await + .result?; Ok(()) } @@ -310,6 +313,7 @@ async fn clear_action_requests( })) }) }) - .await?; + .await + .result?; Ok(()) } diff --git a/core/startos/src/service/effects/callbacks.rs b/core/startos/src/service/effects/callbacks.rs index 19946672c..9f676d12e 100644 --- a/core/startos/src/service/effects/callbacks.rs +++ b/core/startos/src/service/effects/callbacks.rs @@ -8,8 +8,10 @@ use futures::future::join_all; use helpers::NonDetachingJoinHandle; use imbl::{vector, Vector}; use imbl_value::InternedString; +use lazy_static::lazy_static; use models::{HostId, PackageId, ServiceInterfaceId}; use patch_db::json_ptr::JsonPointer; +use patch_db::Revision; use serde::{Deserialize, Serialize}; use tracing::warn; use ts_rs::TS; @@ -37,6 +39,7 @@ struct ServiceCallbackMap { >, get_store: BTreeMap>>, get_status: BTreeMap>, + get_container_ip: BTreeMap>, } impl ServiceCallbacks { @@ -260,13 +263,19 @@ impl ServiceCallbacks { pub fn get_store( &self, package_id: &PackageId, - path: &JsonPointer, + revision: &Revision, ) -> Option { + lazy_static! { + static ref BASE: JsonPointer = "/private/packageStores".parse().unwrap(); + } + let for_pkg = BASE.clone().join_end(&**package_id); self.mutate(|this| { if let Some(watched) = this.get_store.get_mut(package_id) { let mut res = Vec::new(); watched.retain(|ptr, cbs| { - if ptr.starts_with(path) || path.starts_with(ptr) { + let mut full_ptr = for_pkg.clone(); + full_ptr.append(ptr); + if revision.patch.affects_path(&full_ptr) { res.append(cbs); false } else { @@ -280,6 +289,25 @@ impl ServiceCallbacks { .filter(|cb| !cb.0.is_empty()) }) } + + pub(super) fn add_get_container_ip(&self, package_id: PackageId, handler: CallbackHandler) { + self.mutate(|this| { + this.get_container_ip + .entry(package_id) + .or_default() + .push(handler) + }) + } + + #[must_use] + pub fn get_container_ip(&self, package_id: &PackageId) -> Option { + self.mutate(|this| { + this.get_container_ip + .remove(package_id) + .map(CallbackHandlers) + .filter(|cb| !cb.0.is_empty()) + }) + } } pub struct CallbackHandler { diff --git a/core/startos/src/service/effects/control.rs b/core/startos/src/service/effects/control.rs index 4b9817b77..aee3fd9db 100644 --- a/core/startos/src/service/effects/control.rs +++ b/core/startos/src/service/effects/control.rs @@ -46,6 +46,15 @@ pub async fn get_status( let context = context.deref()?; let id = package_id.unwrap_or_else(|| context.seed.id.clone()); let db = context.seed.ctx.db.peek().await; + + if let Some(callback) = callback { + let callback = callback.register(&context.seed.persistent_container); + context.seed.ctx.callbacks.add_get_status( + id.clone(), + super::callbacks::CallbackHandler::new(&context, callback), + ); + } + let status = db .as_public() .as_package_data() @@ -54,14 +63,6 @@ pub async fn get_status( .as_status() .de()?; - if let Some(callback) = callback { - let callback = callback.register(&context.seed.persistent_container); - context.seed.ctx.callbacks.add_get_status( - id, - super::callbacks::CallbackHandler::new(&context, callback), - ); - } - Ok(status) } diff --git a/core/startos/src/service/effects/dependency.rs b/core/startos/src/service/effects/dependency.rs index 2a16b4155..5bf0ee4f7 100644 --- a/core/startos/src/service/effects/dependency.rs +++ b/core/startos/src/service/effects/dependency.rs @@ -249,6 +249,7 @@ pub async fn set_dependencies( .ser(&CurrentDependencies(deps)) }) .await + .result } pub async fn get_dependencies(context: EffectContext) -> Result, Error> { diff --git a/core/startos/src/service/effects/health.rs b/core/startos/src/service/effects/health.rs index c95dea946..26524c1f5 100644 --- a/core/startos/src/service/effects/health.rs +++ b/core/startos/src/service/effects/health.rs @@ -40,6 +40,7 @@ pub async fn set_health( Ok(()) }) }) - .await?; + .await + .result?; Ok(()) } diff --git a/core/startos/src/service/effects/mod.rs b/core/startos/src/service/effects/mod.rs index 48b901d07..43d893efc 100644 --- a/core/startos/src/service/effects/mod.rs +++ b/core/startos/src/service/effects/mod.rs @@ -192,6 +192,4 @@ pub fn handler() -> ParentHandler { "get-system-smtp", from_fn_async(system::get_system_smtp).no_cli(), ) - - // TODO Callbacks } diff --git a/core/startos/src/service/effects/net/host.rs b/core/startos/src/service/effects/net/host.rs index 570d5033d..7039942d7 100644 --- a/core/startos/src/service/effects/net/host.rs +++ b/core/startos/src/service/effects/net/host.rs @@ -27,6 +27,15 @@ pub async fn get_host_info( let db = context.seed.ctx.db.peek().await; let package_id = package_id.unwrap_or_else(|| context.seed.id.clone()); + if let Some(callback) = callback { + let callback = callback.register(&context.seed.persistent_container); + context.seed.ctx.callbacks.add_get_host_info( + package_id.clone(), + host_id.clone(), + CallbackHandler::new(&context, callback), + ); + } + let res = db .as_public() .as_package_data() @@ -35,14 +44,5 @@ pub async fn get_host_info( .map(|m| m.de()) .transpose()?; - if let Some(callback) = callback { - let callback = callback.register(&context.seed.persistent_container); - context.seed.ctx.callbacks.add_get_host_info( - package_id, - host_id, - CallbackHandler::new(&context, callback), - ); - } - Ok(res) } diff --git a/core/startos/src/service/effects/net/info.rs b/core/startos/src/service/effects/net/info.rs index 766c0015f..3b6b38e83 100644 --- a/core/startos/src/service/effects/net/info.rs +++ b/core/startos/src/service/effects/net/info.rs @@ -1,9 +1,55 @@ use std::net::Ipv4Addr; +use models::PackageId; + +use crate::service::effects::callbacks::CallbackHandler; use crate::service::effects::prelude::*; +use crate::service::rpc::CallbackId; use crate::HOST_IP; -pub async fn get_container_ip(context: EffectContext) -> Result { - let context = context.deref()?; - Ok(context.seed.persistent_container.net_service.get_ip().await) +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct GetContainerIpParams { + #[ts(optional)] + package_id: Option, + #[ts(optional)] + callback: Option, +} + +pub async fn get_container_ip( + context: EffectContext, + GetContainerIpParams { + package_id, + callback, + }: GetContainerIpParams, +) -> Result, Error> { + let context = context.deref()?; + + if let Some(package_id) = package_id.filter(|id| id != &context.seed.id) { + if let Some(callback) = callback { + // ip is static for the lifetime of the container, so callback unnecessary for self + let callback = callback.register(&context.seed.persistent_container); + context + .seed + .ctx + .callbacks + .add_get_container_ip(package_id.clone(), CallbackHandler::new(&context, callback)); + } + let Some(svc) = &*context.seed.ctx.services.get(&package_id).await else { + return Ok(None); + }; + + let Some(lxc) = svc.seed.persistent_container.lxc_container.get() else { + return Ok(None); + }; + let res = lxc.ip().await?; + + Ok(Some(res)) + } else { + let Some(lxc) = context.seed.persistent_container.lxc_container.get() else { + return Ok(None); + }; + lxc.ip().await.map(Some) + } } diff --git a/core/startos/src/service/effects/net/interface.rs b/core/startos/src/service/effects/net/interface.rs index 5de9638c4..6cb327fff 100644 --- a/core/startos/src/service/effects/net/interface.rs +++ b/core/startos/src/service/effects/net/interface.rs @@ -42,35 +42,40 @@ pub async fn export_service_interface( interface_type: r#type, }; - context + let res = context .seed .ctx .db .mutate(|db| { - db.as_public_mut() + let ifaces = db + .as_public_mut() .as_package_data_mut() .as_idx_mut(&package_id) .or_not_found(&package_id)? - .as_service_interfaces_mut() - .insert(&id, &service_interface)?; + .as_service_interfaces_mut(); + ifaces.insert(&id, &service_interface)?; Ok(()) }) - .await?; - if let Some(callbacks) = context - .seed - .ctx - .callbacks - .get_service_interface(&(package_id.clone(), id)) - { - callbacks.call(vector![]).await?; - } - if let Some(callbacks) = context - .seed - .ctx - .callbacks - .list_service_interfaces(&package_id) - { - callbacks.call(vector![]).await?; + .await; + res.result?; + + if res.revision.is_some() { + if let Some(callbacks) = context + .seed + .ctx + .callbacks + .get_service_interface(&(package_id.clone(), id)) + { + callbacks.call(vector![]).await?; + } + if let Some(callbacks) = context + .seed + .ctx + .callbacks + .list_service_interfaces(&package_id) + { + callbacks.call(vector![]).await?; + } } Ok(()) @@ -98,6 +103,15 @@ pub async fn get_service_interface( let package_id = package_id.unwrap_or_else(|| context.seed.id.clone()); let db = context.seed.ctx.db.peek().await; + if let Some(callback) = callback { + let callback = callback.register(&context.seed.persistent_container); + context.seed.ctx.callbacks.add_get_service_interface( + package_id.clone(), + service_interface_id.clone(), + CallbackHandler::new(&context, callback), + ); + } + let interface = db .as_public() .as_package_data() @@ -106,15 +120,6 @@ pub async fn get_service_interface( .map(|m| m.de()) .transpose()?; - if let Some(callback) = callback { - let callback = callback.register(&context.seed.persistent_container); - context.seed.ctx.callbacks.add_get_service_interface( - package_id, - service_interface_id, - CallbackHandler::new(&context, callback), - ); - } - Ok(interface) } @@ -137,6 +142,14 @@ pub async fn list_service_interfaces( let context = context.deref()?; let package_id = package_id.unwrap_or_else(|| context.seed.id.clone()); + if let Some(callback) = callback { + let callback = callback.register(&context.seed.persistent_container); + context.seed.ctx.callbacks.add_list_service_interfaces( + package_id.clone(), + CallbackHandler::new(&context, callback), + ); + } + let res = context .seed .ctx @@ -150,15 +163,6 @@ pub async fn list_service_interfaces( .transpose()? .unwrap_or_default(); - if let Some(callback) = callback { - let callback = callback.register(&context.seed.persistent_container); - context - .seed - .ctx - .callbacks - .add_list_service_interfaces(package_id, CallbackHandler::new(&context, callback)); - } - Ok(res) } @@ -176,17 +180,52 @@ pub async fn clear_service_interfaces( let context = context.deref()?; let package_id = context.seed.id.clone(); - context + let res = context .seed .ctx .db .mutate(|db| { + let mut removed = Vec::new(); db.as_public_mut() .as_package_data_mut() .as_idx_mut(&package_id) .or_not_found(&package_id)? .as_service_interfaces_mut() - .mutate(|s| Ok(s.retain(|id, _| except.contains(id)))) + .mutate(|s| { + Ok(s.retain(|id, _| { + if except.contains(id) { + true + } else { + removed.push(id.clone()); + false + } + })) + })?; + Ok(removed) }) - .await + .await; + let removed = res.result?; + + if res.revision.is_some() { + for id in removed { + if let Some(callbacks) = context + .seed + .ctx + .callbacks + .get_service_interface(&(package_id.clone(), id)) + { + callbacks.call(vector![]).await?; + } + } + if let Some(callbacks) = context + .seed + .ctx + .callbacks + .list_service_interfaces(&package_id) + { + callbacks.call(vector![]).await?; + } + } + + Ok(()) } diff --git a/core/startos/src/service/effects/net/ssl.rs b/core/startos/src/service/effects/net/ssl.rs index 66b4fa1e6..2ebd4e006 100644 --- a/core/startos/src/service/effects/net/ssl.rs +++ b/core/startos/src/service/effects/net/ssl.rs @@ -81,7 +81,8 @@ pub async fn get_ssl_certificate( .as_local_certs_mut() .cert_for(&hostnames) }) - .await?; + .await + .result?; let fullchain = match algorithm { Algorithm::Ecdsa => cert.fullchain_nistp256(), Algorithm::Ed25519 => cert.fullchain_ed25519(), @@ -171,7 +172,8 @@ pub async fn get_ssl_key( .as_local_certs_mut() .cert_for(&hostnames) }) - .await?; + .await + .result?; let key = match algorithm { Algorithm::Ecdsa => cert.leaf.keys.nistp256, Algorithm::Ed25519 => cert.leaf.keys.ed25519, diff --git a/core/startos/src/service/effects/store.rs b/core/startos/src/service/effects/store.rs index 39166c333..6ea28488e 100644 --- a/core/startos/src/service/effects/store.rs +++ b/core/startos/src/service/effects/store.rs @@ -65,7 +65,7 @@ pub async fn set_store( ) -> Result<(), Error> { let context = context.deref()?; let package_id = &context.seed.id; - context + let res = context .seed .ctx .db @@ -82,10 +82,13 @@ pub async fn set_store( .with_kind(ErrorKind::ParseDbField)?; model.ser(&model_value) }) - .await?; + .await; + res.result?; - if let Some(callbacks) = context.seed.ctx.callbacks.get_store(package_id, &path) { - callbacks.call(vector![]).await?; + if let Some(revision) = res.revision { + if let Some(callbacks) = context.seed.ctx.callbacks.get_store(package_id, &revision) { + callbacks.call(vector![]).await?; + } } Ok(()) @@ -116,7 +119,8 @@ pub async fn set_data_version( .as_data_version_mut() .ser(&Some(version)) }) - .await?; + .await + .result?; Ok(()) } diff --git a/core/startos/src/service/effects/system.rs b/core/startos/src/service/effects/system.rs index abf0a33c6..abf6f36ad 100644 --- a/core/startos/src/service/effects/system.rs +++ b/core/startos/src/service/effects/system.rs @@ -15,6 +15,16 @@ pub async fn get_system_smtp( GetSystemSmtpParams { callback }: GetSystemSmtpParams, ) -> Result, Error> { let context = context.deref()?; + + if let Some(callback) = callback { + let callback = callback.register(&context.seed.persistent_container); + context + .seed + .ctx + .callbacks + .add_get_system_smtp(CallbackHandler::new(&context, callback)); + } + let res = context .seed .ctx @@ -26,14 +36,5 @@ pub async fn get_system_smtp( .into_smtp() .de()?; - if let Some(callback) = callback { - let callback = callback.register(&context.seed.persistent_container); - context - .seed - .ctx - .callbacks - .add_get_system_smtp(CallbackHandler::new(&context, callback)); - } - Ok(res) } diff --git a/core/startos/src/service/mod.rs b/core/startos/src/service/mod.rs index 3cc8cb67b..8d3db1944 100644 --- a/core/startos/src/service/mod.rs +++ b/core/startos/src/service/mod.rs @@ -171,7 +171,8 @@ impl ServiceRef { Ok(None) } }) - .await? + .await + .result? { let state = pde.state_info.expect_removing()?; if !soft { @@ -336,7 +337,8 @@ impl Service { // TODO: delete s9pk? ctx.db .mutate(|v| v.as_public_mut().as_package_data_mut().remove(id)) - .await?; + .await + .result?; Ok(None) } PackageStateMatchModelRef::Updating(s) => { @@ -392,7 +394,7 @@ impl Service { }) } }) - .await?; + .await.result?; handle_installed(s9pk, entry).await } PackageStateMatchModelRef::Removing(_) | PackageStateMatchModelRef::Restoring(_) => { @@ -419,7 +421,8 @@ impl Service { ctx.db .mutate(|v| v.as_public_mut().as_package_data_mut().remove(id)) - .await?; + .await + .result?; Ok(None) } @@ -550,7 +553,8 @@ impl Service { Ok(()) }) - .await?; + .await + .result?; Ok(service) } diff --git a/core/startos/src/service/persistent_container.rs b/core/startos/src/service/persistent_container.rs index 016cae49f..a20f5c650 100644 --- a/core/startos/src/service/persistent_container.rs +++ b/core/startos/src/service/persistent_container.rs @@ -7,7 +7,7 @@ use std::time::Duration; use futures::future::ready; use futures::Future; use helpers::NonDetachingJoinHandle; -use imbl::Vector; +use imbl::{vector, Vector}; use imbl_value::InternedString; use models::{ImageId, ProcedureName, VolumeId}; use rpc_toolkit::{Empty, Server, ShutdownHandle}; @@ -297,10 +297,16 @@ impl PersistentContainer { .await?; } } + let ip = lxc_container.ip().await?; let net_service = ctx .net_controller - .create_service(s9pk.as_manifest().id.clone(), lxc_container.ip().await?) + .create_service(s9pk.as_manifest().id.clone(), ip) .await?; + if let Some(callbacks) = ctx.callbacks.get_container_ip(&s9pk.as_manifest().id) { + callbacks + .call(vector![Value::String(Arc::new(ip.to_string()))]) + .await?; + } Ok(Self { s9pk, lxc_container: OnceCell::new_with(Some(lxc_container)), diff --git a/core/startos/src/service/service_actor.rs b/core/startos/src/service/service_actor.rs index a56c92288..422198441 100644 --- a/core/startos/src/service/service_actor.rs +++ b/core/startos/src/service/service_actor.rs @@ -99,7 +99,8 @@ async fn service_actor_loop( } Ok(None) }) - .await?; + .await + .result?; if let Some((previous, new_state)) = major_changes_state { if let Some(callbacks) = seed.ctx.callbacks.get_status(id) { callbacks diff --git a/core/startos/src/service/service_map.rs b/core/startos/src/service/service_map.rs index cec0c8564..777be2bd7 100644 --- a/core/startos/src/service/service_map.rs +++ b/core/startos/src/service/service_map.rs @@ -117,7 +117,8 @@ impl ServiceMap { } Ok(()) }) - .await?; + .await + .result?; } } shutdown_err?; @@ -174,54 +175,62 @@ impl ServiceMap { let mut reload_guard = ServiceRefReloadGuard::new(ctx.clone(), id.clone(), op_name); reload_guard - .handle(ctx.db.mutate({ - let manifest = manifest.clone(); - let id = id.clone(); - let install_progress = progress.snapshot(); - move |db| { - if let Some(pde) = db.as_public_mut().as_package_data_mut().as_idx_mut(&id) { - let prev = pde.as_state_info().expect_installed()?.de()?; - pde.as_state_info_mut() - .ser(&PackageState::Updating(UpdatingState { - manifest: prev.manifest, - installing_info: InstallingInfo { - new_manifest: manifest, - progress: install_progress, - }, - }))?; - } else { - let installing = InstallingState { - installing_info: InstallingInfo { - new_manifest: manifest, - progress: install_progress, - }, - }; - db.as_public_mut().as_package_data_mut().insert( - &id, - &PackageDataEntry { - state_info: if restoring { - PackageState::Restoring(installing) - } else { - PackageState::Installing(installing) - }, - data_version: None, - status: MainStatus::Stopped, - registry: None, - developer_key: Pem::new(developer_key), - icon, - last_backup: None, - current_dependencies: Default::default(), - actions: Default::default(), - requested_actions: Default::default(), - service_interfaces: Default::default(), - hosts: Default::default(), - store_exposed_dependents: Default::default(), - }, - )?; - }; - Ok(()) - } - })) + .handle(async { + ctx.db + .mutate({ + let manifest = manifest.clone(); + let id = id.clone(); + let install_progress = progress.snapshot(); + move |db| { + if let Some(pde) = + db.as_public_mut().as_package_data_mut().as_idx_mut(&id) + { + let prev = pde.as_state_info().expect_installed()?.de()?; + pde.as_state_info_mut().ser(&PackageState::Updating( + UpdatingState { + manifest: prev.manifest, + installing_info: InstallingInfo { + new_manifest: manifest, + progress: install_progress, + }, + }, + ))?; + } else { + let installing = InstallingState { + installing_info: InstallingInfo { + new_manifest: manifest, + progress: install_progress, + }, + }; + db.as_public_mut().as_package_data_mut().insert( + &id, + &PackageDataEntry { + state_info: if restoring { + PackageState::Restoring(installing) + } else { + PackageState::Installing(installing) + }, + data_version: None, + status: MainStatus::Stopped, + registry: None, + developer_key: Pem::new(developer_key), + icon, + last_backup: None, + current_dependencies: Default::default(), + actions: Default::default(), + requested_actions: Default::default(), + service_interfaces: Default::default(), + hosts: Default::default(), + store_exposed_dependents: Default::default(), + }, + )?; + }; + Ok(()) + } + }) + .await + .result + }) .await?; Ok(async move { @@ -425,7 +434,8 @@ impl ServiceRefReloadInfo { (), ) }) - .await?; + .await + .result?; } Ok(()) } diff --git a/core/startos/src/setup.rs b/core/startos/src/setup.rs index 186fbb0c4..a1976e285 100644 --- a/core/startos/src/setup.rs +++ b/core/startos/src/setup.rs @@ -98,7 +98,8 @@ async fn setup_init( .ser(&account.password)?; Ok(account) }) - .await?; + .await + .result?; Ok((account, init_result)) } diff --git a/core/startos/src/shutdown.rs b/core/startos/src/shutdown.rs index 4e45f74c0..12deff743 100644 --- a/core/startos/src/shutdown.rs +++ b/core/startos/src/shutdown.rs @@ -84,7 +84,8 @@ pub async fn shutdown(ctx: RpcContext) -> Result<(), Error> { .as_shutting_down_mut() .ser(&true) }) - .await?; + .await + .result?; ctx.shutdown .send(Some(Shutdown { export_args: Some((ctx.disk_guid.clone(), Path::new(DATA_DIR).to_owned())), @@ -104,7 +105,8 @@ pub async fn restart(ctx: RpcContext) -> Result<(), Error> { .as_restarting_mut() .ser(&true) }) - .await?; + .await + .result?; ctx.shutdown .send(Some(Shutdown { export_args: Some((ctx.disk_guid.clone(), Path::new(DATA_DIR).to_owned())), diff --git a/core/startos/src/ssh.rs b/core/startos/src/ssh.rs index 301ed8721..0f236b813 100644 --- a/core/startos/src/ssh.rs +++ b/core/startos/src/ssh.rs @@ -145,7 +145,8 @@ pub async fn add(ctx: RpcContext, AddParams { key }: AddParams) -> Result Result< .ser(&enable)?; Ok(()) }) - .await?; + .await + .result?; Ok(()) } @@ -168,7 +169,8 @@ pub async fn governor( .as_governor_mut() .ser(&Some(set)) }) - .await?; + .await + .result?; } let current = ctx .db @@ -856,7 +858,8 @@ pub async fn set_system_smtp(ctx: RpcContext, smtp: SmtpValue) -> Result<(), Err .as_smtp_mut() .ser(&smtp) }) - .await?; + .await + .result?; if let Some(callbacks) = ctx.callbacks.get_system_smtp() { callbacks.call(vector![to_value(&smtp)?]).await?; } @@ -870,7 +873,8 @@ pub async fn clear_system_smtp(ctx: RpcContext) -> Result<(), Error> { .as_smtp_mut() .ser(&None) }) - .await?; + .await + .result?; if let Some(callbacks) = ctx.callbacks.get_system_smtp() { callbacks.call(vector![Value::Null]).await?; } diff --git a/core/startos/src/update/mod.rs b/core/startos/src/update/mod.rs index 7daa4f3b2..bf11983ca 100644 --- a/core/startos/src/update/mod.rs +++ b/core/startos/src/update/mod.rs @@ -288,7 +288,8 @@ async fn maybe_do_update( .ser(&status)?; Ok(status) }) - .await?; + .await + .result?; if status.updated { return Err(Error::new( @@ -332,12 +333,10 @@ async fn maybe_do_update( status_info.as_update_progress_mut().ser(&None)?; status_info.as_updated_mut().ser(&true) }) - .await?; - progress_task.await.with_kind(ErrorKind::Unknown)??; - CIRCLE_OF_5THS_SHORT - .play() .await - .expect("could not play sound"); + .result?; + progress_task.await.with_kind(ErrorKind::Unknown)??; + CIRCLE_OF_5THS_SHORT.play().await.log_err(); } Err(e) => { let err_string = format!("Update was not successful because of {}", e); @@ -358,24 +357,13 @@ async fn maybe_do_update( ) }) .await - .unwrap(); + .result + .log_err(); // TODO: refactor sound lib to make compound tempos easier to deal with - UPDATE_FAILED_1 - .play() - .await - .expect("could not play song: update failed 1"); - UPDATE_FAILED_2 - .play() - .await - .expect("could not play song: update failed 2"); - UPDATE_FAILED_3 - .play() - .await - .expect("could not play song: update failed 3"); - UPDATE_FAILED_4 - .play() - .await - .expect("could not play song: update failed 4"); + UPDATE_FAILED_1.play().await.log_err(); + UPDATE_FAILED_2.play().await.log_err(); + UPDATE_FAILED_3.play().await.log_err(); + UPDATE_FAILED_4.play().await.log_err(); } } Ok::<(), Error>(()) diff --git a/core/startos/src/version/mod.rs b/core/startos/src/version/mod.rs index 01f9c5e86..087f64e2f 100644 --- a/core/startos/src/version/mod.rs +++ b/core/startos/src/version/mod.rs @@ -36,8 +36,9 @@ mod v0_3_6_alpha_13; mod v0_3_6_alpha_14; mod v0_3_6_alpha_15; mod v0_3_6_alpha_16; +mod v0_3_6_alpha_17; -pub type Current = v0_3_6_alpha_16::Version; // VERSION_BUMP +pub type Current = v0_3_6_alpha_17::Version; // VERSION_BUMP impl Current { #[instrument(skip(self, db))] @@ -54,7 +55,8 @@ impl Current { rollback_to_unchecked(&from, &self, &mut db)?; Ok::<_, Error>((db, ())) }) - .await?; + .await + .result?; } Ordering::Less => { let pre_ups = PreUps::load(&from, &self).await?; @@ -62,7 +64,8 @@ impl Current { migrate_from_unchecked(&from, &self, pre_ups, &mut db)?; Ok::<_, Error>((to_value(&from_value::(db.clone())?)?, ())) }) - .await?; + .await + .result?; } Ordering::Equal => (), } @@ -103,7 +106,8 @@ pub async fn post_init( .as_post_init_migration_todos_mut() .mutate(|m| Ok(m.remove(&version.0.semver()))) }) - .await?; + .await + .result?; progress += 1; } } @@ -135,7 +139,8 @@ enum Version { V0_3_6_alpha_13(Wrapper), V0_3_6_alpha_14(Wrapper), V0_3_6_alpha_15(Wrapper), - V0_3_6_alpha_16(Wrapper), // VERSION_BUMP + V0_3_6_alpha_16(Wrapper), + V0_3_6_alpha_17(Wrapper), // VERSION_BUMP Other(exver::Version), } @@ -175,7 +180,8 @@ impl Version { Self::V0_3_6_alpha_13(v) => DynVersion(Box::new(v.0)), Self::V0_3_6_alpha_14(v) => DynVersion(Box::new(v.0)), Self::V0_3_6_alpha_15(v) => DynVersion(Box::new(v.0)), - Self::V0_3_6_alpha_16(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP + Self::V0_3_6_alpha_16(v) => DynVersion(Box::new(v.0)), + Self::V0_3_6_alpha_17(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP Self::Other(v) => { return Err(Error::new( eyre!("unknown version {v}"), @@ -207,7 +213,8 @@ impl Version { Version::V0_3_6_alpha_13(Wrapper(x)) => x.semver(), Version::V0_3_6_alpha_14(Wrapper(x)) => x.semver(), Version::V0_3_6_alpha_15(Wrapper(x)) => x.semver(), - Version::V0_3_6_alpha_16(Wrapper(x)) => x.semver(), // VERSION_BUMP + Version::V0_3_6_alpha_16(Wrapper(x)) => x.semver(), + Version::V0_3_6_alpha_17(Wrapper(x)) => x.semver(), // VERSION_BUMP Version::Other(x) => x.clone(), } } diff --git a/core/startos/src/version/v0_3_6_alpha_17.rs b/core/startos/src/version/v0_3_6_alpha_17.rs new file mode 100644 index 000000000..2d58bab5a --- /dev/null +++ b/core/startos/src/version/v0_3_6_alpha_17.rs @@ -0,0 +1,36 @@ +use exver::{PreReleaseSegment, VersionRange}; + +use super::v0_3_5::V0_3_0_COMPAT; +use super::{v0_3_6_alpha_16, VersionT}; +use crate::prelude::*; + +lazy_static::lazy_static! { + static ref V0_3_6_alpha_17: exver::Version = exver::Version::new( + [0, 3, 6], + [PreReleaseSegment::String("alpha".into()), 17.into()] + ); +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct Version; + +impl VersionT for Version { + type Previous = v0_3_6_alpha_16::Version; + type PreUpRes = (); + + async fn pre_up(self) -> Result { + Ok(()) + } + fn semver(self) -> exver::Version { + V0_3_6_alpha_17.clone() + } + fn compat(self) -> &'static VersionRange { + &V0_3_0_COMPAT + } + fn up(self, _db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> { + Ok(()) + } + fn down(self, _db: &mut Value) -> Result<(), Error> { + Ok(()) + } +} diff --git a/core/startos/src/version/v0_3_6_alpha_6.rs b/core/startos/src/version/v0_3_6_alpha_6.rs index 7d62773ea..61d6675e4 100644 --- a/core/startos/src/version/v0_3_6_alpha_6.rs +++ b/core/startos/src/version/v0_3_6_alpha_6.rs @@ -45,7 +45,8 @@ impl VersionT for Version { )?; Ok(()) }) - .await?; + .await + .result?; Ok(()) } fn down(self, _db: &mut Value) -> Result<(), Error> { diff --git a/image-recipe/build.sh b/image-recipe/build.sh index eaf5e8382..8dc4a57d8 100755 --- a/image-recipe/build.sh +++ b/image-recipe/build.sh @@ -147,7 +147,7 @@ if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then fi cat > config/archives/backports.pref <<- EOF -Package: * +Package: linux-image-* Pin: release n=${IB_SUITE}-backports Pin-Priority: 500 EOF diff --git a/patch-db b/patch-db index 0df18c651..d1362bdcd 160000 --- a/patch-db +++ b/patch-db @@ -1 +1 @@ -Subproject commit 0df18c651f2311e2e26f3e6c8535a9e40b71502f +Subproject commit d1362bdcd9138125a8079e58ed9cc54358aa56f1 diff --git a/sdk/base/lib/Effects.ts b/sdk/base/lib/Effects.ts index b7c67af12..941e57cf9 100644 --- a/sdk/base/lib/Effects.ts +++ b/sdk/base/lib/Effects.ts @@ -130,7 +130,10 @@ export type Effects = { callback?: () => void }): Promise /** Returns the IP address of the container */ - getContainerIp(): Promise + getContainerIp(options: { + packageId?: PackageId + callback?: () => void + }): Promise /** Returns the IP address of StartOS */ getOsIp(): Promise // interface diff --git a/sdk/base/lib/osBindings/GetContainerIpParams.ts b/sdk/base/lib/osBindings/GetContainerIpParams.ts new file mode 100644 index 000000000..2c84b4960 --- /dev/null +++ b/sdk/base/lib/osBindings/GetContainerIpParams.ts @@ -0,0 +1,8 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { CallbackId } from "./CallbackId" +import type { PackageId } from "./PackageId" + +export type GetContainerIpParams = { + packageId?: PackageId + callback?: CallbackId +} diff --git a/sdk/base/lib/osBindings/index.ts b/sdk/base/lib/osBindings/index.ts index e2ab33033..305128b60 100644 --- a/sdk/base/lib/osBindings/index.ts +++ b/sdk/base/lib/osBindings/index.ts @@ -80,6 +80,7 @@ export { ForgetInterfaceParams } from "./ForgetInterfaceParams" export { FullIndex } from "./FullIndex" export { FullProgress } from "./FullProgress" export { GetActionInputParams } from "./GetActionInputParams" +export { GetContainerIpParams } from "./GetContainerIpParams" export { GetHostInfoParams } from "./GetHostInfoParams" export { GetOsAssetParams } from "./GetOsAssetParams" export { GetOsVersionParams } from "./GetOsVersionParams" diff --git a/sdk/base/lib/test/startosTypeValidation.test.ts b/sdk/base/lib/test/startosTypeValidation.test.ts index f6656e55d..3a66de1e1 100644 --- a/sdk/base/lib/test/startosTypeValidation.test.ts +++ b/sdk/base/lib/test/startosTypeValidation.test.ts @@ -7,6 +7,7 @@ import { ClearCallbacksParams, ClearServiceInterfacesParams, GetActionInputParams, + GetContainerIpParams, GetStatusParams, RequestActionParams, RunActionParams, @@ -77,7 +78,7 @@ describe("startosTypeValidation ", () => { set: {} as any, // as SetStoreParams, }, getSystemSmtp: {} as WithCallback, - getContainerIp: undefined, + getContainerIp: {} as WithCallback, getOsIp: undefined, getServicePortForward: {} as GetServicePortForwardParams, clearServiceInterfaces: {} as ClearServiceInterfacesParams, diff --git a/sdk/package/lib/StartSdk.ts b/sdk/package/lib/StartSdk.ts index 42229f436..799adcb3c 100644 --- a/sdk/package/lib/StartSdk.ts +++ b/sdk/package/lib/StartSdk.ts @@ -72,7 +72,7 @@ import * as actions from "../../base/lib/actions" import { setupInit } from "./inits/setupInit" import * as fs from "node:fs/promises" -export const OSVersion = testTypeVersion("0.3.6-alpha.16") +export const OSVersion = testTypeVersion("0.3.6-alpha.17") // prettier-ignore type AnyNeverCond = @@ -104,7 +104,10 @@ export class StartSdk { | "getHostInfo" type MainUsedEffects = "setMainStatus" | "setHealth" type CallbackEffects = "constRetry" | "clearCallbacks" - type AlreadyExposed = "getSslCertificate" | "getSystemSmtp" + type AlreadyExposed = + | "getSslCertificate" + | "getSystemSmtp" + | "getContainerIp" // prettier-ignore type StartSdkEffectWrapper = { @@ -123,7 +126,6 @@ export class StartSdk { getServicePortForward: (effects, ...args) => effects.getServicePortForward(...args), clearBindings: (effects, ...args) => effects.clearBindings(...args), - getContainerIp: (effects, ...args) => effects.getContainerIp(...args), getOsIp: (effects, ...args) => effects.getOsIp(...args), getSslKey: (effects, ...args) => effects.getSslKey(...args), setDataVersion: (effects, ...args) => effects.setDataVersion(...args), @@ -191,7 +193,61 @@ export class StartSdk { opts: { packageId: PackageId }, ) => getServiceInterfaces(effects, opts), }, - + getContainerIp: ( + effects: T.Effects, + options: Omit< + Parameters[0], + "callback" + > = {}, + ) => { + async function* watch() { + while (true) { + let callback: () => void = () => {} + const waitForNext = new Promise((resolve) => { + callback = resolve + }) + yield await effects.getContainerIp({ ...options, callback }) + await waitForNext + } + } + return { + const: () => + effects.getContainerIp({ + ...options, + callback: + effects.constRetry && + (() => effects.constRetry && effects.constRetry()), + }), + once: () => effects.getContainerIp(options), + watch, + onChange: ( + callback: ( + value: string | null, + error?: Error, + ) => void | Promise, + ) => { + ;(async () => { + for await (const value of watch()) { + try { + await callback(value) + } catch (e) { + console.error( + "callback function threw an error @ getContainerIp.onChange", + e, + ) + } + } + })() + .catch((e) => callback(null, e)) + .catch((e) => + console.error( + "callback function threw an error @ getContainerIp.onChange", + e, + ), + ) + }, + } + }, store: { get: ( effects: E, diff --git a/sdk/package/package-lock.json b/sdk/package/package-lock.json index 58aec58ad..e6eee8cac 100644 --- a/sdk/package/package-lock.json +++ b/sdk/package/package-lock.json @@ -1,12 +1,12 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-beta.18", + "version": "0.3.6-beta.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@start9labs/start-sdk", - "version": "0.3.6-beta.18", + "version": "0.3.6-beta.19", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/sdk/package/package.json b/sdk/package/package.json index ef9116d92..69d2c1f97 100644 --- a/sdk/package/package.json +++ b/sdk/package/package.json @@ -1,6 +1,6 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-beta.18", + "version": "0.3.6-beta.19", "description": "Software development kit to facilitate packaging services for StartOS", "main": "./package/lib/index.js", "types": "./package/lib/index.d.ts", diff --git a/web/package-lock.json b/web/package-lock.json index 36379429c..b7eb9b040 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "startos-ui", - "version": "0.3.6-alpha.16", + "version": "0.3.6-alpha.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "startos-ui", - "version": "0.3.6-alpha.16", + "version": "0.3.6-alpha.17", "license": "MIT", "dependencies": { "@angular/animations": "^14.1.0", diff --git a/web/package.json b/web/package.json index 5b7402d5f..622124743 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "startos-ui", - "version": "0.3.6-alpha.16", + "version": "0.3.6-alpha.17", "author": "Start9 Labs, Inc", "homepage": "https://start9.com/", "license": "MIT", From b8ff331ccc99a6404b477d57d864be8d920806b7 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 21 Mar 2025 11:17:05 -0600 Subject: [PATCH 2/5] add callback for getHost --- core/startos/src/context/rpc.rs | 7 +++---- core/startos/src/net/net_controller.rs | 27 ++++++++++++++++++-------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index 8bd138b86..94fdd10c5 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -1,4 +1,3 @@ -use std::backtrace; use std::collections::{BTreeMap, BTreeSet}; use std::future::Future; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; @@ -63,7 +62,7 @@ pub struct RpcContextSeed { pub lxc_manager: Arc, pub open_authed_continuations: OpenAuthedContinuations>, pub rpc_continuations: RpcContinuations, - pub callbacks: ServiceCallbacks, + pub callbacks: Arc, pub wifi_manager: Option>>, pub current_secret: Arc, pub client: Client, @@ -231,6 +230,7 @@ impl RpcContext { sync_db: watch::Sender::new(db.sequence().await), db, account: RwLock::new(account), + callbacks: net_controller.callbacks.clone(), net_controller, os_net_service, s9pk_arch: if config.multi_arch_s9pks.unwrap_or(false) { @@ -245,7 +245,6 @@ impl RpcContext { lxc_manager: Arc::new(LxcManager::new()), open_authed_continuations: OpenAuthedContinuations::new(), rpc_continuations: RpcContinuations::new(), - callbacks: Default::default(), wifi_manager: wifi_interface .clone() .map(|i| Arc::new(RwLock::new(WpaCli::init(i)))), @@ -492,7 +491,7 @@ impl Drop for RpcContext { let count = Arc::strong_count(&self.0) - 1; tracing::info!("RpcContext dropped. {} left.", count); if count > 0 { - tracing::debug!("{}", backtrace::Backtrace::force_capture()); + tracing::debug!("{}", std::backtrace::Backtrace::force_capture()); tracing::debug!("{:?}", eyre!("")) } } diff --git a/core/startos/src/net/net_controller.rs b/core/startos/src/net/net_controller.rs index ca687bedb..403e3ac36 100644 --- a/core/startos/src/net/net_controller.rs +++ b/core/startos/src/net/net_controller.rs @@ -3,7 +3,7 @@ use std::net::{Ipv4Addr, SocketAddr}; use std::sync::{Arc, Weak}; use color_eyre::eyre::eyre; -use imbl::OrdMap; +use imbl::{vector, OrdMap}; use imbl_value::InternedString; use ipnet::IpNet; use models::{HostId, OptionExt, PackageId}; @@ -26,6 +26,7 @@ use crate::net::tor::TorController; use crate::net::utils::ipv6_is_local; use crate::net::vhost::{AlpnInfo, TargetInfo, VHostController}; use crate::prelude::*; +use crate::service::effects::callbacks::ServiceCallbacks; use crate::util::serde::MaybeUtf8String; use crate::HOST_IP; @@ -37,6 +38,7 @@ pub struct NetController { pub(super) dns: DnsController, pub(super) forward: LanPortForwardController, pub(super) server_hostnames: Vec>, + pub(crate) callbacks: Arc, } impl NetController { @@ -66,6 +68,7 @@ impl NetController { // LAN mDNS Some(hostname.local_domain_name()), ], + callbacks: Arc::new(ServiceCallbacks::default()), }) } @@ -80,7 +83,7 @@ impl NetController { let res = NetService::new(NetServiceData { id: Some(package), ip, - dns, + _dns: dns, controller: Arc::downgrade(self), binds: BTreeMap::new(), })?; @@ -94,7 +97,7 @@ impl NetController { let service = NetService::new(NetServiceData { id: None, ip: [127, 0, 0, 1].into(), - dns, + _dns: dns, controller: Arc::downgrade(self), binds: BTreeMap::new(), })?; @@ -131,7 +134,7 @@ struct HostBinds { pub struct NetServiceData { id: Option, ip: Ipv4Addr, - dns: Arc<()>, + _dns: Arc<()>, controller: Weak, binds: BTreeMap, } @@ -576,14 +579,22 @@ impl NetServiceData { } } - ctrl.db + let res = ctrl + .db .mutate(|db| { host_for(db, self.id.as_ref(), &id)? .as_hostname_info_mut() .ser(&hostname_info) }) - .await - .result?; + .await; + res.result?; + if let Some(pkg_id) = self.id.as_ref() { + if res.revision.is_some() { + if let Some(cbs) = ctrl.callbacks.get_host_info(&(pkg_id.clone(), id)) { + cbs.call(vector![]).await?; + } + } + } Ok(()) } @@ -637,7 +648,7 @@ impl NetService { data: Arc::new(Mutex::new(NetServiceData { id: None, ip: Ipv4Addr::new(0, 0, 0, 0), - dns: Default::default(), + _dns: Default::default(), controller: Default::default(), binds: BTreeMap::new(), })), From 0016b4bd7276877871e2061a4091586c4dcb7df6 Mon Sep 17 00:00:00 2001 From: Lucy <12953208+elvece@users.noreply.github.com> Date: Fri, 28 Mar 2025 12:59:48 -0400 Subject: [PATCH 3/5] allow ids to include numbers (#2857) --- core/models/src/id/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/models/src/id/mod.rs b/core/models/src/id/mod.rs index 0c313973f..16c047c8e 100644 --- a/core/models/src/id/mod.rs +++ b/core/models/src/id/mod.rs @@ -26,7 +26,7 @@ pub use service_interface::ServiceInterfaceId; pub use volume::VolumeId; lazy_static::lazy_static! { - static ref ID_REGEX: Regex = Regex::new("^[a-z]+(-[a-z0-9]+)*$").unwrap(); + static ref ID_REGEX: Regex = Regex::new("^[a-z0-9]+(-[a-z0-9]+)*$").unwrap(); pub static ref SYSTEM_ID: Id = Id(InternedString::intern("x_system")); } From 6ecaeb4fde1d9dc92e509aec17b7050d34e1aa11 Mon Sep 17 00:00:00 2001 From: Mariusz Kogen Date: Sun, 30 Mar 2025 16:04:34 +0200 Subject: [PATCH 4/5] fix initiall setup as user and clear messaging (#2848) * fix initiall setup as user and clear messaging * fix this and that :) * add IPv6 support to validate_ip function * Use vpn-clearnet as name for the interface * Rebrand and finish with docs link * set static clearnet name * Magic clearnet to the end :D * change the command name * the name is magic-clearnet * wireguard-vps-proxy-setup * one more fix --- ...wg-vps-setup => wireguard-vps-proxy-setup} | 236 ++++++++++++++++-- debian/postinst | 2 +- 2 files changed, 213 insertions(+), 25 deletions(-) rename build/lib/scripts/{wg-vps-setup => wireguard-vps-proxy-setup} (53%) diff --git a/build/lib/scripts/wg-vps-setup b/build/lib/scripts/wireguard-vps-proxy-setup similarity index 53% rename from build/lib/scripts/wg-vps-setup rename to build/lib/scripts/wireguard-vps-proxy-setup index 6c630bb46..f9f654fe3 100755 --- a/build/lib/scripts/wg-vps-setup +++ b/build/lib/scripts/wireguard-vps-proxy-setup @@ -1,5 +1,33 @@ #!/bin/bash +# ============================================================================= +# Wireguard VPS Proxy Setup +# ============================================================================= +# +# This script automates the setup of a WireGuard VPN server on a remote VPS +# for StartOS Clearnet functionality. It handles: +# +# 1. SSH key-based authentication setup +# 2. Root access configuration (if needed) +# 3. WireGuard server installation +# 4. Configuration file generation and import +# +# Usage: +# wireguard-vps-proxy-setup [-h] [-i IP] [-u USERNAME] [-p PORT] [-k SSH_KEY] +# +# Options: +# -h Show help message +# -i VPS IP address +# -u SSH username (default: root) +# -p SSH port (default: 22) +# -k Path to custom SSH private key +# +# Example: +# wireguard-vps-proxy-setup -i 110.18.1.1 -u debian +# +# Note: This script requires root privileges and will auto-elevate if needed. +# ============================================================================= + # Colors for better output RED='\033[0;31m' GREEN='\033[0;32m' @@ -8,7 +36,7 @@ YELLOW='\033[1;33m' NC='\033[0;37m' # No Color # --- Constants --- -readonly WIREGUARD_INSTALL_URL="https://raw.githubusercontent.com/start9labs/wg-vps-setup/master/wireguard-install.sh" +readonly WIREGUARD_INSTALL_URL="https://raw.githubusercontent.com/start9labs/wireguard-vps-proxy-setup/master/wireguard-install.sh" readonly SSH_KEY_DIR="/home/start9/.ssh" readonly SSH_KEY_NAME="id_ed25519" readonly SSH_PRIVATE_KEY="$SSH_KEY_DIR/$SSH_KEY_NAME" @@ -31,7 +59,7 @@ check_root() { print_banner() { echo -e "${BLUE}" echo "================================================" - echo -e " ${NC}StartOS WireGuard VPS Setup Tool${BLUE} " + echo -e " ${NC}Wireguard VPS Proxy Setup${BLUE} " echo "================================================" echo -e "${NC}" } @@ -51,23 +79,53 @@ print_usage() { # Function to display end message display_end_message() { echo -e "\n${BLUE}------------------------------------------------------------------${NC}" - echo -e "${NC}WireGuard server setup complete!" - echo -e "${BLUE}------------------------------------------------------------------${NC}" - echo -e "\n${YELLOW}To expose your services to the Clearnet, use the following commands on your StartOS system (replace placeholders):${NC}" - echo -e "\n ${YELLOW}1. Initialize ACME (This only needs to be done once):${NC}" - echo " start-cli net acme init --provider=letsencrypt --contact=mailto:your-email@example.com" - echo -e "\n ${YELLOW}2. Expose 'hello-world' on port 80 through VPS:${NC}" - echo " start-cli package host hello-world binding ui-multi set-public 80" - echo -e "\n ${YELLOW}3. Add a domain to your 'hello-world' service:${NC}" - echo " start-cli package host hello-world address ui-multi domain add your-domain.example.com --acme=letsencrypt" - echo -e "\n ${YELLOW}Replace '${NC}your-email@example.com${YELLOW}' with your actual email address, '${NC}your-domain.example.com${YELLOW}' with your actual domain and '${NC}hello-world${YELLOW}' with your actual service id.${NC}" + echo -e "${GREEN}Wireguard VPS Proxy server setup complete!${NC}" echo -e "${BLUE}------------------------------------------------------------------${NC}" + echo -e "\n${GREEN}Clearnet functionality has been enabled via VPS (${VPS_IP})${NC}" + echo -e "\n${YELLOW}Next steps:${NC}" + echo -e "Visit https://docs.start9.com to complete the Clearnet setup" + echo -e "\n${BLUE}------------------------------------------------------------------${NC}" } # Function to validate IP address validate_ip() { local ip=$1 + # IPv4 validation if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + # Additional IPv4 validation to ensure each octet is <= 255 + local IFS='.' + read -ra ADDR <<< "$ip" + for i in "${ADDR[@]}"; do + if [ "$i" -gt 255 ]; then + return 1 + fi + done + return 0 + # IPv6 validation + elif [[ $ip =~ ^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){6}:[0-9a-fA-F]{1,4}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){5}(:[0-9a-fA-F]{1,4}){1,2}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){4}(:[0-9a-fA-F]{1,4}){1,3}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){3}(:[0-9a-fA-F]{1,4}){1,4}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){2}(:[0-9a-fA-F]{1,4}){1,5}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1}(:[0-9a-fA-F]{1,4}){1,6}$ ]] || \ + [[ $ip =~ ^::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}$ ]] || \ + [[ $ip =~ ^[0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,4}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,3}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,2}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,1}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,7}:$ ]] || \ + [[ $ip =~ ^::([0-9a-fA-F]{1,4}:){0,7}[0-9a-fA-F]{1,4}$ ]] || \ + [[ $ip =~ ^[0-9a-fA-F]{1,4}::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,6}(:[0-9a-fA-F]{1,4}){1,1}$ ]] || \ + [[ $ip =~ ^([0-9a-fA-F]{1,4}:){1,7}:$ ]] || \ + [[ $ip =~ ^::$ ]]; then return 0 else return 1 @@ -90,6 +148,15 @@ configure_ssh_key_auth() { echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config fi + # Enable root login + if grep -q "^#PermitRootLogin" /etc/ssh/sshd_config; then + sed -i "s/^#PermitRootLogin.*/PermitRootLogin yes/" /etc/ssh/sshd_config + elif grep -q "^PermitRootLogin" /etc/ssh/sshd_config; then + sed -i "s/^PermitRootLogin.*/PermitRootLogin yes/" /etc/ssh/sshd_config + else + echo "PermitRootLogin yes" >> /etc/ssh/sshd_config + fi + # Configure AuthorizedKeysFile if needed if grep -q "^#AuthorizedKeysFile" /etc/ssh/sshd_config; then sed -i "s/^#AuthorizedKeysFile.*/AuthorizedKeysFile .ssh\/authorized_keys .ssh\/authorized_keys2/" /etc/ssh/sshd_config @@ -203,7 +270,7 @@ install_wireguard() { fi # Run the remote install script and let it complete - if ! ssh -o ConnectTimeout=60 -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" -t "$SSH_USER@$VPS_IP" "bash -c 'export TERM=xterm-256color; export STARTOS_HOSTNAME=$(hostname); bash ~/wireguard-install.sh'"; then + if ! ssh -o ConnectTimeout=60 -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" -t "$SSH_USER@$VPS_IP" "bash -c 'export TERM=xterm-256color; export STARTOS_HOSTNAME=clearnet; bash ~/wireguard-install.sh'"; then echo -e "${RED}WireGuard installation failed on remote server.${NC}" return 1 fi @@ -218,6 +285,74 @@ install_wireguard() { return 0 } +# Function to enable root login via SSH +enable_root_login() { + echo -e "${BLUE}Checking and configuring root SSH access...${NC}" + + # Try to modify sshd config using sudo + if ! ssh -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" "$SSH_USER@$VPS_IP" ' + # Check if we can use sudo without password + if ! sudo -n true 2>/dev/null; then + echo -e "\033[1;33mNOTE: You may be prompted for your sudo password.\033[0m" + fi + + # Check if user is in sudo group + if ! groups | grep -q sudo; then + echo -e "\033[1;31mError: Your user is not in the sudo group. Root access cannot be configured.\033[0m" + exit 1 + fi + + # Backup sshd config + sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak + + # Enable root login with SSH keys only + if sudo grep -q "^PermitRootLogin" /etc/ssh/sshd_config; then + sudo sed -i "s/^PermitRootLogin.*/PermitRootLogin prohibit-password/" /etc/ssh/sshd_config + else + echo "PermitRootLogin prohibit-password" | sudo tee -a /etc/ssh/sshd_config + fi + + # Ensure password authentication is disabled + if sudo grep -q "^PasswordAuthentication" /etc/ssh/sshd_config; then + sudo sed -i "s/^PasswordAuthentication.*/PasswordAuthentication no/" /etc/ssh/sshd_config + else + echo "PasswordAuthentication no" | sudo tee -a /etc/ssh/sshd_config + fi + + # Set up root SSH directory and keys + echo -e "\033[1;33mSetting up root SSH access...\033[0m" + sudo mkdir -p /root/.ssh + sudo cp ~/.ssh/authorized_keys /root/.ssh/ + sudo chown -R root:root /root/.ssh + sudo chmod 700 /root/.ssh + sudo chmod 600 /root/.ssh/authorized_keys + + # Reload SSH service + sudo systemctl reload sshd + + # Verify the changes + if ! sudo grep -q "^PermitRootLogin prohibit-password" /etc/ssh/sshd_config; then + echo -e "\033[1;31mError: Failed to verify root login configuration.\033[0m" + exit 1 + fi + + # Test root SSH access + if ! sudo -n true 2>/dev/null; then + echo -e "\033[1;33mNOTE: Please try to log in as root now using your SSH key.\033[0m" + echo -e "\033[1;33mIf successful, run this script again without the -u parameter.\033[0m" + else + echo -e "\033[1;32mRoot SSH access has been configured successfully!\033[0m" + fi + '; then + echo -e "${RED}Failed to configure root SSH access.${NC}" + return 1 + fi + + echo -e "${GREEN}Root SSH access has been configured successfully!${NC}" + echo -e "${YELLOW}Please try to log in as root now using your SSH key. If successful, run this script again without the -u parameter.${NC}" + return 0 +} + # --- Main Script --- # Initialize variables VPS_IP="" @@ -311,27 +446,80 @@ echo -e "${GREEN}SSH key-based authentication configured successfully!${NC}" # Test SSH connection using key-based authentication echo -e "\nTesting SSH connection with key-based authentication..." -if ! ssh -q -o BatchMode=yes -o ConnectTimeout=5 -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" "$SSH_USER@$VPS_IP" 'grep -q "^PubkeyAuthentication yes" /etc/ssh/sshd_config'; then - echo -e "\n${RED}SSH key-based authentication is not enabled on your VPS.${NC}" - echo -e "\n${YELLOW}Would you like this script to automatically enable SSH key authentication? (y/N):${NC} " - read -r answer +if ! ssh -q -o BatchMode=yes -o ConnectTimeout=5 -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" "$SSH_USER@$VPS_IP" 'exit'; then + echo -e "${RED}SSH connection test failed. Please check your credentials and try again.${NC}" + exit 1 +fi +# If we're connecting as a non-root user, set up root access first +if [ "$SSH_USER" != "root" ]; then + echo -e "\n${YELLOW}You are connecting as a non-root user. This script needs to enable root SSH access.${NC}" + echo -e "${YELLOW}This is a one-time setup that will allow direct root login for WireGuard installation.${NC}" + echo -n -e "${YELLOW}Would you like to proceed? (y/N): ${NC}" + read -r answer + if [[ "$answer" =~ ^[Yy]$ ]]; then - configure_ssh_key_auth + if enable_root_login; then + echo -e "\n${BLUE}------------------------------------------------------------------${NC}" + echo -e "${GREEN}Root SSH access has been configured successfully!${NC}" + echo -e "${YELLOW}Please run this script again without the -u parameter to continue setup.${NC}" + echo -e "${BLUE}------------------------------------------------------------------${NC}" + exit 0 + else + echo -e "${RED}Failed to configure root SSH access. Please check your sudo privileges and try again.${NC}" + exit 1 + fi else echo -e "\n${BLUE}------------------------------------------------------------------${NC}" - echo -e "${YELLOW}To manually enable SSH key authentication:${NC}" + echo -e "${YELLOW}To manually configure SSH for root access:${NC}" echo -e "\n ${YELLOW}1. Connect to your VPS and edit sshd_config:${NC}" - echo " nano /etc/ssh/sshd_config" - echo -e "\n ${YELLOW}2. Find and uncomment or add the line:${NC}" + echo " sudo nano /etc/ssh/sshd_config" + echo -e "\n ${YELLOW}2. Find and uncomment or add these lines:${NC}" echo " PubkeyAuthentication yes" + echo " PermitRootLogin yes" + echo " AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2" echo -e "\n ${YELLOW}3. Restart the SSH service:${NC}" - echo " systemctl restart sshd" + echo " sudo systemctl restart sshd" + echo -e "\n ${YELLOW}4. Copy your SSH key to root user:${NC}" + echo " sudo mkdir -p /root/.ssh" + echo " sudo cp ~/.ssh/authorized_keys /root/.ssh/" + echo " sudo chown -R root:root /root/.ssh" + echo " sudo chmod 700 /root/.ssh" + echo " sudo chmod 600 /root/.ssh/authorized_keys" echo -e "${BLUE}------------------------------------------------------------------${NC}" - echo -e "\n${YELLOW}Please enable SSH key authentication and run this script again.${NC}" + echo -e "\n${YELLOW}After completing these steps, run this script again without the -u parameter.${NC}" exit 1 fi fi + +# Check if root login is permitted when connecting as root +if [ "$SSH_USER" = "root" ]; then + # Check for both "yes" and "prohibit-password" as valid root login settings + if ! ssh -q -o BatchMode=yes -o ConnectTimeout=5 -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" "$SSH_USER@$VPS_IP" 'grep -q "^PermitRootLogin.*\(yes\|prohibit-password\)" /etc/ssh/sshd_config'; then + echo -e "\n${RED}Root SSH login is not enabled on your VPS.${NC}" + echo -e "\n${YELLOW}Would you like this script to automatically enable root SSH access? (y/N):${NC} " + read -r answer + + if [[ "$answer" =~ ^[Yy]$ ]]; then + configure_ssh_key_auth + else + echo -e "\n${BLUE}------------------------------------------------------------------${NC}" + echo -e "${YELLOW}To manually configure SSH for root access:${NC}" + echo -e "\n ${YELLOW}1. Connect to your VPS and edit sshd_config:${NC}" + echo " sudo nano /etc/ssh/sshd_config" + echo -e "\n ${YELLOW}2. Find and uncomment or add these lines:${NC}" + echo " PubkeyAuthentication yes" + echo " PermitRootLogin prohibit-password" + echo " AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2" + echo -e "\n ${YELLOW}3. Restart the SSH service:${NC}" + echo " sudo systemctl restart sshd" + echo -e "${BLUE}------------------------------------------------------------------${NC}" + echo -e "\n${YELLOW}Please enable root SSH access and run this script again.${NC}" + exit 1 + fi + fi +fi + echo -e "${GREEN}SSH connection successful with key-based authentication!${NC}" # Download the WireGuard install script locally @@ -364,4 +552,4 @@ fi # Import the configuration if ! import_wireguard_config "$CONFIG_NAME"; then echo -e "${RED}StartOS configuration import failed or skipped!${NC}" -fi +fi \ No newline at end of file diff --git a/debian/postinst b/debian/postinst index d29fcfb86..2404ac5b3 100755 --- a/debian/postinst +++ b/debian/postinst @@ -109,7 +109,7 @@ rm -rf /var/lib/tor/* ln -sf /usr/lib/startos/scripts/chroot-and-upgrade /usr/bin/chroot-and-upgrade ln -sf /usr/lib/startos/scripts/tor-check /usr/bin/tor-check ln -sf /usr/lib/startos/scripts/gather-debug-info /usr/bin/gather-debug-info -ln -sf /usr/lib/startos/scripts/wg-vps-setup /usr/bin/wg-vps-setup +ln -sf /usr/lib/startos/scripts/wireguard-vps-proxy-setup /usr/bin/wireguard-vps-proxy-setup echo "fs.inotify.max_user_watches=1048576" > /etc/sysctl.d/97-startos.conf From 20d3b5288c76342411b5d484b404941e8246b652 Mon Sep 17 00:00:00 2001 From: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Date: Mon, 7 Apr 2025 13:55:38 -0600 Subject: [PATCH 5/5] sdk tweaks (#2858) * sdk tweaks * beta.20 * alpha.18 --- .../SystemForEmbassy/polyfillEffects.ts | 37 +++++++------------ core/Cargo.lock | 2 +- core/startos/Cargo.toml | 2 +- core/startos/src/version/mod.rs | 12 ++++-- core/startos/src/version/v0_3_6_alpha_18.rs | 36 ++++++++++++++++++ image-recipe/build.sh | 6 +-- sdk/base/lib/interfaces/Host.ts | 12 ------ sdk/package/lib/StartSdk.ts | 29 +++++++++++---- sdk/package/lib/inits/setupInit.ts | 10 +++++ sdk/package/lib/inits/setupInstall.ts | 18 +++++++-- sdk/package/lib/mainFn/Daemons.ts | 2 +- sdk/package/lib/util/SubContainer.ts | 22 +++++++++++ sdk/package/package-lock.json | 4 +- sdk/package/package.json | 2 +- web/package-lock.json | 4 +- web/package.json | 2 +- 16 files changed, 139 insertions(+), 61 deletions(-) create mode 100644 core/startos/src/version/v0_3_6_alpha_18.rs diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts index 0099bea92..88e9d7ae6 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts @@ -9,6 +9,7 @@ import { Manifest } from "./matchManifest" import { DockerProcedureContainer } from "./DockerProcedureContainer" import * as cp from "child_process" import { Effects } from "../../../Models/Effects" +import { Mounts } from "@start9labs/start-sdk/package/lib/mainFn/Mounts" export const execFile = promisify(cp.execFile) export const polyfillEffects = ( effects: Effects, @@ -111,7 +112,7 @@ export const polyfillEffects = ( effects, { imageId: manifest.main.image }, commands, - {}, + { mounts: Mounts.of() }, commands.join(" "), ) .then((x: any) => ({ @@ -168,17 +169,12 @@ export const polyfillEffects = ( { imageId: manifest.main.image }, commands, { - mounts: [ - { - mountpoint: "/drive", - options: { - type: "volume", - id: input.volumeId, - subpath: null, - readonly: false, - }, - }, - ], + mounts: Mounts.of().addVolume( + input.volumeId, + null, + "/drive", + false, + ), }, commands.join(" "), ) @@ -210,17 +206,12 @@ export const polyfillEffects = ( { imageId: manifest.main.image }, commands, { - mounts: [ - { - mountpoint: "/drive", - options: { - type: "volume", - id: input.volumeId, - subpath: null, - readonly: false, - }, - }, - ], + mounts: Mounts.of().addVolume( + input.volumeId, + null, + "/drive", + false, + ), }, commands.join(" "), ) diff --git a/core/Cargo.lock b/core/Cargo.lock index 7ec339ded..d7b4f3cf4 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -5952,7 +5952,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "start-os" -version = "0.3.6-alpha.17" +version = "0.3.6-alpha.18" dependencies = [ "aes 0.7.5", "async-acme", diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index e0bace2af..f6ae4b880 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -14,7 +14,7 @@ keywords = [ name = "start-os" readme = "README.md" repository = "https://github.com/Start9Labs/start-os" -version = "0.3.6-alpha.17" # VERSION_BUMP +version = "0.3.6-alpha.18" # VERSION_BUMP license = "MIT" [lib] diff --git a/core/startos/src/version/mod.rs b/core/startos/src/version/mod.rs index 087f64e2f..c9791b1d1 100644 --- a/core/startos/src/version/mod.rs +++ b/core/startos/src/version/mod.rs @@ -37,8 +37,9 @@ mod v0_3_6_alpha_14; mod v0_3_6_alpha_15; mod v0_3_6_alpha_16; mod v0_3_6_alpha_17; +mod v0_3_6_alpha_18; -pub type Current = v0_3_6_alpha_17::Version; // VERSION_BUMP +pub type Current = v0_3_6_alpha_18::Version; // VERSION_BUMP impl Current { #[instrument(skip(self, db))] @@ -140,7 +141,8 @@ enum Version { V0_3_6_alpha_14(Wrapper), V0_3_6_alpha_15(Wrapper), V0_3_6_alpha_16(Wrapper), - V0_3_6_alpha_17(Wrapper), // VERSION_BUMP + V0_3_6_alpha_17(Wrapper), + V0_3_6_alpha_18(Wrapper), // VERSION_BUMP Other(exver::Version), } @@ -181,7 +183,8 @@ impl Version { Self::V0_3_6_alpha_14(v) => DynVersion(Box::new(v.0)), Self::V0_3_6_alpha_15(v) => DynVersion(Box::new(v.0)), Self::V0_3_6_alpha_16(v) => DynVersion(Box::new(v.0)), - Self::V0_3_6_alpha_17(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP + Self::V0_3_6_alpha_17(v) => DynVersion(Box::new(v.0)), + Self::V0_3_6_alpha_18(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP Self::Other(v) => { return Err(Error::new( eyre!("unknown version {v}"), @@ -214,7 +217,8 @@ impl Version { Version::V0_3_6_alpha_14(Wrapper(x)) => x.semver(), Version::V0_3_6_alpha_15(Wrapper(x)) => x.semver(), Version::V0_3_6_alpha_16(Wrapper(x)) => x.semver(), - Version::V0_3_6_alpha_17(Wrapper(x)) => x.semver(), // VERSION_BUMP + Version::V0_3_6_alpha_17(Wrapper(x)) => x.semver(), + Version::V0_3_6_alpha_18(Wrapper(x)) => x.semver(), // VERSION_BUMP Version::Other(x) => x.clone(), } } diff --git a/core/startos/src/version/v0_3_6_alpha_18.rs b/core/startos/src/version/v0_3_6_alpha_18.rs new file mode 100644 index 000000000..2d2c7ad32 --- /dev/null +++ b/core/startos/src/version/v0_3_6_alpha_18.rs @@ -0,0 +1,36 @@ +use exver::{PreReleaseSegment, VersionRange}; + +use super::v0_3_5::V0_3_0_COMPAT; +use super::{v0_3_6_alpha_17, VersionT}; +use crate::prelude::*; + +lazy_static::lazy_static! { + static ref V0_3_6_alpha_18: exver::Version = exver::Version::new( + [0, 3, 6], + [PreReleaseSegment::String("alpha".into()), 18.into()] + ); +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct Version; + +impl VersionT for Version { + type Previous = v0_3_6_alpha_17::Version; + type PreUpRes = (); + + async fn pre_up(self) -> Result { + Ok(()) + } + fn semver(self) -> exver::Version { + V0_3_6_alpha_18.clone() + } + fn compat(self) -> &'static VersionRange { + &V0_3_0_COMPAT + } + fn up(self, _db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> { + Ok(()) + } + fn down(self, _db: &mut Value) -> Result<(), Error> { + Ok(()) + } +} diff --git a/image-recipe/build.sh b/image-recipe/build.sh index 8dc4a57d8..d5c820591 100755 --- a/image-recipe/build.sh +++ b/image-recipe/build.sh @@ -61,7 +61,7 @@ PLATFORM_CONFIG_EXTRAS=() if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then PLATFORM_CONFIG_EXTRAS+=( --firmware-binary false ) PLATFORM_CONFIG_EXTRAS+=( --firmware-chroot false ) - PLATFORM_CONFIG_EXTRAS+=( --linux-packages linux-image-6.6.51+rpt ) + PLATFORM_CONFIG_EXTRAS+=( --linux-packages linux-image-6.12.20+rpt ) PLATFORM_CONFIG_EXTRAS+=( --linux-flavours "rpi-v8 rpi-2712" ) elif [ "${IB_TARGET_PLATFORM}" = "rockchip64" ]; then PLATFORM_CONFIG_EXTRAS+=( --linux-flavours rockchip64 ) @@ -206,8 +206,8 @@ if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then echo "Configuring raspi kernel '\$v'" extract-ikconfig "/usr/lib/modules/\$v/kernel/kernel/configs.ko.xz" > /boot/config-\$v done - mkinitramfs -c gzip -o /boot/initramfs8 6.6.74-v8+ - mkinitramfs -c gzip -o /boot/initramfs_2712 6.6.74-v8-16k+ + mkinitramfs -c gzip -o /boot/initramfs8 6.12.20-v8+ + mkinitramfs -c gzip -o /boot/initramfs_2712 6.12.20-v8-16k+ fi useradd --shell /bin/bash -G startos -m start9 diff --git a/sdk/base/lib/interfaces/Host.ts b/sdk/base/lib/interfaces/Host.ts index 53419357f..be9525f1c 100644 --- a/sdk/base/lib/interfaces/Host.ts +++ b/sdk/base/lib/interfaces/Host.ts @@ -33,18 +33,6 @@ export const knownProtocols = { secure: { ssl: false }, defaultPort: 22, }, - bitcoin: { - secure: { ssl: false }, - defaultPort: 8333, - }, - lightning: { - secure: { ssl: true }, - defaultPort: 9735, - }, - grpc: { - secure: { ssl: true }, - defaultPort: 50051, - }, dns: { secure: { ssl: false }, defaultPort: 53, diff --git a/sdk/package/lib/StartSdk.ts b/sdk/package/lib/StartSdk.ts index 799adcb3c..3da6732e5 100644 --- a/sdk/package/lib/StartSdk.ts +++ b/sdk/package/lib/StartSdk.ts @@ -47,7 +47,12 @@ import { GetSystemSmtp } from "./util" import { nullIfEmpty } from "./util" import { getServiceInterface, getServiceInterfaces } from "./util" import { getStore } from "./store/getStore" -import { CommandOptions, MountOptions, SubContainer } from "./util/SubContainer" +import { + CommandOptions, + ExitError, + MountOptions, + SubContainer, +} from "./util/SubContainer" import { splitCommand } from "./util" import { Mounts } from "./mainFn/Mounts" import { setupDependencies } from "../../base/lib/dependencies/setupDependencies" @@ -72,7 +77,7 @@ import * as actions from "../../base/lib/actions" import { setupInit } from "./inits/setupInit" import * as fs from "node:fs/promises" -export const OSVersion = testTypeVersion("0.3.6-alpha.17") +export const OSVersion = testTypeVersion("0.3.6-alpha.18") // prettier-ignore type AnyNeverCond = @@ -286,7 +291,7 @@ export class StartSdk { }, command: T.CommandType, options: CommandOptions & { - mounts?: { mountpoint: string; options: MountOptions }[] + mounts: Mounts }, /** * A name to use to refer to the ephemeral subcontainer for debugging purposes @@ -583,7 +588,10 @@ export class StartSdk { }) * ``` */ - setupInstall: (fn: InstallFn) => Install.of(fn), + setupInstall: ( + fn: InstallFn, + preFn?: InstallFn, + ) => Install.of(fn, preFn), /** * @description Use this function to determine how this service will be hosted and served. The function executes on service install, service update, and inputSpec save. * @@ -1132,7 +1140,7 @@ export async function runCommand( image: { imageId: keyof Manifest["images"] & T.ImageId; sharedRun?: boolean }, command: T.CommandType, options: CommandOptions & { - mounts?: { mountpoint: string; options: MountOptions }[] + mounts: Mounts }, name?: string, ): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> { @@ -1150,7 +1158,7 @@ export async function runCommand( return SubContainer.with( effects, image, - options.mounts || [], + options.mounts.build(), name || commands .map((c) => { @@ -1161,6 +1169,13 @@ export async function runCommand( } }) .join(" "), - (subcontainer) => subcontainer.exec(commands), + async (subcontainer) => { + const res = await subcontainer.exec(commands) + if (res.exitCode || res.exitSignal) { + throw new ExitError(commands[0], res) + } else { + return res + } + }, ) } diff --git a/sdk/package/lib/inits/setupInit.ts b/sdk/package/lib/inits/setupInit.ts index 8d30f5b1f..21cfd1641 100644 --- a/sdk/package/lib/inits/setupInit.ts +++ b/sdk/package/lib/inits/setupInit.ts @@ -3,6 +3,7 @@ import { ExtendedVersion } from "../../../base/lib/exver" import { UpdateServiceInterfaces } from "../../../base/lib/interfaces/setupInterfaces" import { ExposedStorePaths } from "../../../base/lib/types" import * as T from "../../../base/lib/types" +import { StorePath } from "../util" import { VersionGraph } from "../version/VersionGraph" import { Install } from "./setupInstall" import { Uninstall } from "./setupUninstall" @@ -16,6 +17,7 @@ export function setupInit( effects: T.Effects }) => Promise, actions: Actions, + initStore: Store, exposedStore: ExposedStorePaths, ): { packageInit: T.ExpectedExports.packageInit @@ -53,6 +55,14 @@ export function setupInit( } }, containerInit: async (opts) => { + const prev = await opts.effects.getDataVersion() + if (!prev) { + await opts.effects.store.set({ + path: "" as StorePath, + value: initStore, + }) + await install.preInstall(opts) + } await setServiceInterfaces({ ...opts, }) diff --git a/sdk/package/lib/inits/setupInstall.ts b/sdk/package/lib/inits/setupInstall.ts index 38a96a00b..708367e20 100644 --- a/sdk/package/lib/inits/setupInstall.ts +++ b/sdk/package/lib/inits/setupInstall.ts @@ -4,11 +4,15 @@ export type InstallFn = (opts: { effects: T.Effects }) => Promise export class Install { - private constructor(readonly fn: InstallFn) {} + private constructor( + readonly fn: InstallFn, + readonly preFn?: InstallFn, + ) {} static of( fn: InstallFn, + preFn?: InstallFn, ) { - return new Install(fn) + return new Install(fn, preFn) } async install({ effects }: Parameters[0]) { @@ -16,10 +20,18 @@ export class Install { effects, }) } + + async preInstall({ effects }: Parameters[0]) { + this.preFn && + (await this.preFn({ + effects, + })) + } } export function setupInstall( fn: InstallFn, + preFn?: InstallFn, ) { - return Install.of(fn) + return Install.of(fn, preFn) } diff --git a/sdk/package/lib/mainFn/Daemons.ts b/sdk/package/lib/mainFn/Daemons.ts index d471394c7..9732d6e7a 100644 --- a/sdk/package/lib/mainFn/Daemons.ts +++ b/sdk/package/lib/mainFn/Daemons.ts @@ -172,7 +172,7 @@ export class Daemons daemon, daemonIndex, options.requires - .map((x) => this.ids.indexOf(id as any)) + .map((x) => this.ids.indexOf(x)) .filter((x) => x >= 0) .map((id) => this.healthDaemons[id]), id, diff --git a/sdk/package/lib/util/SubContainer.ts b/sdk/package/lib/util/SubContainer.ts index 912c3112a..13d91535e 100644 --- a/sdk/package/lib/util/SubContainer.ts +++ b/sdk/package/lib/util/SubContainer.ts @@ -467,3 +467,25 @@ export type MountOptionsBackup = { function wait(time: number) { return new Promise((resolve) => setTimeout(resolve, time)) } + +export class ExitError extends Error { + constructor( + readonly command: string, + readonly result: { + exitCode: number | null + exitSignal: T.Signals | null + stdout: string | Buffer + stderr: string | Buffer + }, + ) { + let message: string + if (result.exitCode) { + message = `${command} failed with exit code ${result.exitCode}: ${result.stderr}` + } else if (result.exitSignal) { + message = `${command} terminated with signal ${result.exitSignal}: ${result.stderr}` + } else { + message = `${command} succeeded: ${result.stdout}` + } + super(message) + } +} diff --git a/sdk/package/package-lock.json b/sdk/package/package-lock.json index e6eee8cac..4641278c2 100644 --- a/sdk/package/package-lock.json +++ b/sdk/package/package-lock.json @@ -1,12 +1,12 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-beta.19", + "version": "0.3.6-beta.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@start9labs/start-sdk", - "version": "0.3.6-beta.19", + "version": "0.3.6-beta.20", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/sdk/package/package.json b/sdk/package/package.json index 69d2c1f97..55a3d29d7 100644 --- a/sdk/package/package.json +++ b/sdk/package/package.json @@ -1,6 +1,6 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-beta.19", + "version": "0.3.6-beta.20", "description": "Software development kit to facilitate packaging services for StartOS", "main": "./package/lib/index.js", "types": "./package/lib/index.d.ts", diff --git a/web/package-lock.json b/web/package-lock.json index b7eb9b040..a53fcb035 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "startos-ui", - "version": "0.3.6-alpha.17", + "version": "0.3.6-alpha.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "startos-ui", - "version": "0.3.6-alpha.17", + "version": "0.3.6-alpha.18", "license": "MIT", "dependencies": { "@angular/animations": "^14.1.0", diff --git a/web/package.json b/web/package.json index 622124743..3f617db14 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "startos-ui", - "version": "0.3.6-alpha.17", + "version": "0.3.6-alpha.18", "author": "Start9 Labs, Inc", "homepage": "https://start9.com/", "license": "MIT",