diff --git a/Makefile b/Makefile index dbe10d677..1dffd3629 100644 --- a/Makefile +++ b/Makefile @@ -184,7 +184,7 @@ container-runtime/node_modules: container-runtime/package.json container-runtime npm --prefix container-runtime ci touch container-runtime/node_modules -sdk/lib/osBindings: core/startos/bindings +sdk/lib/osBindings: $(shell core/startos/bindings) mkdir -p sdk/lib/osBindings ls core/startos/bindings/*.ts | sed 's/core\/startos\/bindings\/\([^.]*\)\.ts/export { \1 } from ".\/\1";/g' > core/startos/bindings/index.ts npm --prefix sdk exec -- prettier --config ./sdk/package.json -w ./core/startos/bindings/*.ts diff --git a/build/dpkg-deps/depends b/build/dpkg-deps/depends index 38d9b1a58..7519cff1b 100644 --- a/build/dpkg-deps/depends +++ b/build/dpkg-deps/depends @@ -18,6 +18,7 @@ grub-common htop httpdirfs iotop +iptables iw jq libyajl2 @@ -34,6 +35,7 @@ network-manager nvme-cli nyx openssh-server +podman postgresql psmisc qemu-guest-agent diff --git a/build/lib/scripts/add-apt-sources b/build/lib/scripts/add-apt-sources index 638d8dad6..9d4f54a28 100755 --- a/build/lib/scripts/add-apt-sources +++ b/build/lib/scripts/add-apt-sources @@ -4,6 +4,3 @@ set -e curl -fsSL https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --dearmor -o- > /usr/share/keyrings/tor-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org bullseye main" > /etc/apt/sources.list.d/tor.list - -curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o- > /usr/share/keyrings/docker-archive-keyring.gpg -echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian bullseye stable" > /etc/apt/sources.list.d/docker.list diff --git a/build/lib/scripts/chroot-and-upgrade b/build/lib/scripts/chroot-and-upgrade index ef3208806..7adaaaccb 100755 --- a/build/lib/scripts/chroot-and-upgrade +++ b/build/lib/scripts/chroot-and-upgrade @@ -33,6 +33,7 @@ set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters if [ -z "$NO_SYNC" ]; then echo 'Syncing...' umount -R /media/startos/next 2> /dev/null + umount -R /media/startos/upper 2> /dev/null rm -rf /media/startos/upper /media/startos/next mkdir /media/startos/upper mount -t tmpfs tmpfs /media/startos/upper @@ -105,4 +106,5 @@ if [ "$CHROOT_RES" -eq 0 ]; then fi umount -R /media/startos/next +umount -R /media/startos/upper rm -rf /media/startos/upper /media/startos/next \ No newline at end of file diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts index b4a5f5829..c06395a17 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts @@ -17,7 +17,7 @@ export class DockerProcedureContainer { data: DockerProcedure, volumes: { [id: VolumeId]: Volume }, ) { - const overlay = await Overlay.of(effects, data.image) + const overlay = await Overlay.of(effects, { id: data.image }) if (data.mounts) { const mounts = data.mounts diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts index 7e18c69f1..23be7071f 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts @@ -53,7 +53,7 @@ export class MainLoop { } const daemon = await daemons.runDaemon()( this.effects, - this.system.manifest.main.image, + { id: this.system.manifest.main.image }, currentCommand, { overlay: dockerProcedureContainer.overlay, diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts index 66a303e7b..30e572d97 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts @@ -96,7 +96,7 @@ export class PolyfillEffects implements oet.Effects { return startSdk .runCommand( this.effects, - this.manifest.main.image, + { id: this.manifest.main.image }, [command, ...(args || [])], {}, ) @@ -118,7 +118,7 @@ export class PolyfillEffects implements oet.Effects { const daemon = dockerProcedureContainer.then((dockerProcedureContainer) => daemons.runDaemon()( this.effects, - this.manifest.main.image, + { id: this.manifest.main.image }, [input.command, ...(input.args || [])], { overlay: dockerProcedureContainer.overlay, @@ -143,7 +143,7 @@ export class PolyfillEffects implements oet.Effects { await startSdk .runCommand( this.effects, - this.manifest.main.image, + { id: this.manifest.main.image }, ["chown", "--recursive", input.uid, `/drive/${input.path}`], { mounts: [ @@ -178,7 +178,7 @@ export class PolyfillEffects implements oet.Effects { await startSdk .runCommand( this.effects, - this.manifest.main.image, + { id: this.manifest.main.image }, ["chmod", "--recursive", input.mode, `/drive/${input.path}`], { mounts: [ diff --git a/core/models/src/id/mod.rs b/core/models/src/id/mod.rs index 11644c71d..10882d4f1 100644 --- a/core/models/src/id/mod.rs +++ b/core/models/src/id/mod.rs @@ -28,8 +28,13 @@ lazy_static::lazy_static! { pub static ref SYSTEM_ID: Id = Id(InternedString::intern("x_system")); } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct Id(InternedString); +impl std::fmt::Debug for Id { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} impl TryFrom for Id { type Error = InvalidId; fn try_from(value: InternedString) -> Result { diff --git a/core/startos/src/db/model/public.rs b/core/startos/src/db/model/public.rs index d79948c6f..fe85056f9 100644 --- a/core/startos/src/db/model/public.rs +++ b/core/startos/src/db/model/public.rs @@ -76,6 +76,7 @@ impl Public { ntp_synced: false, zram: true, governor: None, + smtp: None, }, package_data: AllPackageData::default(), ui: serde_json::from_str(include_str!(concat!( @@ -135,6 +136,7 @@ pub struct ServerInfo { #[serde(default)] pub zram: bool, pub governor: Option, + pub smtp: Option } #[derive(Debug, Deserialize, Serialize, HasModel, TS)] diff --git a/core/startos/src/db/prelude.rs b/core/startos/src/db/prelude.rs index d40c3c17c..419b356ef 100644 --- a/core/startos/src/db/prelude.rs +++ b/core/startos/src/db/prelude.rs @@ -220,7 +220,7 @@ where } pub fn upsert(&mut self, key: &T::Key, value: F) -> Result<&mut Model, Error> where - F: FnOnce() -> T::Value, + F: FnOnce() -> Result, { use serde::ser::Error; match &mut self.value { @@ -233,7 +233,7 @@ where s.as_ref().index_or_insert(v) }); if !exists { - res.ser(&value())?; + res.ser(&value()?)?; } Ok(res) } diff --git a/core/startos/src/init.rs b/core/startos/src/init.rs index 361288c10..694d4e3a3 100644 --- a/core/startos/src/init.rs +++ b/core/startos/src/init.rs @@ -348,6 +348,12 @@ pub async fn init(cfg: &ServerConfig) -> Result { }) .await?; + Command::new("systemctl") + .arg("start") + .arg("lxc-net.service") + .invoke(ErrorKind::Lxc) + .await?; + crate::version::init(&db).await?; db.mutate(|d| { diff --git a/core/startos/src/net/host/address.rs b/core/startos/src/net/host/address.rs index cb3b485f6..d9e2f4206 100644 --- a/core/startos/src/net/host/address.rs +++ b/core/startos/src/net/host/address.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use torut::onion::OnionAddressV3; use ts_rs::TS; -#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, TS)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, TS)] #[serde(rename_all = "camelCase")] #[serde(tag = "kind")] #[ts(export)] diff --git a/core/startos/src/net/host/mod.rs b/core/startos/src/net/host/mod.rs index 2d50df15a..2d8599ba9 100644 --- a/core/startos/src/net/host/mod.rs +++ b/core/startos/src/net/host/mod.rs @@ -1,10 +1,12 @@ use std::collections::{BTreeMap, BTreeSet}; use imbl_value::InternedString; -use models::HostId; +use models::{HostId, PackageId}; use serde::{Deserialize, Serialize}; +use torut::onion::{OnionAddressV3, TorSecretKeyV3}; use ts_rs::TS; +use crate::db::model::DatabaseModel; use crate::net::forward::AvailablePorts; use crate::net::host::address::HostAddress; use crate::net::host::binding::{BindInfo, BindOptions}; @@ -64,25 +66,72 @@ impl Map for HostInfo { } } -impl Model { +pub fn host_for<'a>( + db: &'a mut DatabaseModel, + package_id: &PackageId, + host_id: &HostId, + host_kind: HostKind, +) -> Result<&'a mut Model, Error> { + fn host_info<'a>( + db: &'a mut DatabaseModel, + package_id: &PackageId, + ) -> Result<&'a mut Model, Error> { + Ok::<_, Error>( + db.as_public_mut() + .as_package_data_mut() + .as_idx_mut(package_id) + .or_not_found(package_id)? + .as_hosts_mut(), + ) + } + let tor_key = if host_info(db, package_id)?.as_idx(host_id).is_none() { + Some( + db.as_private_mut() + .as_key_store_mut() + .as_onion_mut() + .new_key()?, + ) + } else { + None + }; + host_info(db, package_id)?.upsert(host_id, || { + let mut h = Host::new(host_kind); + h.addresses.insert(HostAddress::Onion { + address: tor_key + .or_not_found("generated tor key")? + .public() + .get_onion_address(), + }); + Ok(h) + }) +} + +impl Model { + pub fn set_kind(&mut self, kind: HostKind) -> Result<(), Error> { + match (self.as_kind().de()?, kind) { + (HostKind::Multi, HostKind::Multi) => Ok(()), + } + } pub fn add_binding( &mut self, available_ports: &mut AvailablePorts, - kind: HostKind, - id: &HostId, internal_port: u16, options: BindOptions, ) -> Result<(), Error> { - self.upsert(id, || Host::new(kind))? - .as_bindings_mut() - .mutate(|b| { - let info = if let Some(info) = b.remove(&internal_port) { - info.update(available_ports, options)? - } else { - BindInfo::new(available_ports, options)? - }; - b.insert(internal_port, info); - Ok(()) - }) // TODO: handle host kind change + self.as_bindings_mut().mutate(|b| { + let info = if let Some(info) = b.remove(&internal_port) { + info.update(available_ports, options)? + } else { + BindInfo::new(available_ports, options)? + }; + b.insert(internal_port, info); + Ok(()) + }) + } +} + +impl HostInfo { + pub fn get_host_primary(&self, host_id: &HostId) -> Option { + self.0.get(&host_id).and_then(|h| h.primary.clone()) } } diff --git a/core/startos/src/net/net_controller.rs b/core/startos/src/net/net_controller.rs index aae98ae96..205cc4e36 100644 --- a/core/startos/src/net/net_controller.rs +++ b/core/startos/src/net/net_controller.rs @@ -16,7 +16,7 @@ use crate::net::dns::DnsController; use crate::net::forward::LanPortForwardController; use crate::net::host::address::HostAddress; use crate::net::host::binding::{AddSslOptions, BindOptions}; -use crate::net::host::{Host, HostKind}; +use crate::net::host::{host_for, Host, HostKind}; use crate::net::tor::TorController; use crate::net::vhost::{AlpnInfo, VHostController}; use crate::prelude::*; @@ -162,7 +162,7 @@ impl NetController { } } -#[derive(Default)] +#[derive(Default, Debug)] struct HostBinds { lan: BTreeMap, Arc<()>)>, tor: BTreeMap, Vec>)>, @@ -193,25 +193,17 @@ impl NetService { internal_port: u16, options: BindOptions, ) -> Result<(), Error> { - let id_ref = &id; + dbg!("bind", &kind, &id, internal_port, &options); let pkg_id = &self.id; let host = self .net_controller()? .db - .mutate(|d| { - let mut ports = d.as_private().as_available_ports().de()?; - let hosts = d - .as_public_mut() - .as_package_data_mut() - .as_idx_mut(pkg_id) - .or_not_found(pkg_id)? - .as_hosts_mut(); - hosts.add_binding(&mut ports, kind, &id, internal_port, options)?; - let host = hosts - .as_idx(&id) - .or_not_found(lazy_format!("Host {id_ref} for {pkg_id}"))? - .de()?; - d.as_private_mut().as_available_ports_mut().ser(&ports)?; + .mutate(|db| { + let mut ports = db.as_private().as_available_ports().de()?; + let host = host_for(db, pkg_id, &id, kind)?; + host.add_binding(&mut ports, internal_port, options)?; + let host = host.de()?; + db.as_private_mut().as_available_ports_mut().ser(&ports)?; Ok(host) }) .await?; @@ -219,6 +211,8 @@ impl NetService { } async fn update(&mut self, id: HostId, host: Host) -> Result<(), Error> { + dbg!(&host); + dbg!(&self.binds); let ctrl = self.net_controller()?; let binds = { if !self.binds.contains_key(&id) { diff --git a/core/startos/src/registry/os/asset/add.rs b/core/startos/src/registry/os/asset/add.rs index 6e259e314..d2c20e711 100644 --- a/core/startos/src/registry/os/asset/add.rs +++ b/core/startos/src/registry/os/asset/add.rs @@ -9,7 +9,7 @@ use futures::{FutureExt, TryStreamExt}; use helpers::NonDetachingJoinHandle; use imbl_value::InternedString; use itertools::Itertools; -use rpc_toolkit::{from_fn_async, CallRemote, Context, HandlerArgs, HandlerExt, ParentHandler}; +use rpc_toolkit::{from_fn_async, Context, HandlerArgs, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha512}; use ts_rs::TS; @@ -106,9 +106,11 @@ async fn add_asset( .as_idx_mut(&version) .or_not_found(&version)?, ) - .upsert(&platform, || RegistryAsset { - url, - signature_info: SignatureInfo::new(SIG_CONTEXT), + .upsert(&platform, || { + Ok(RegistryAsset { + url, + signature_info: SignatureInfo::new(SIG_CONTEXT), + }) })? .as_signature_info_mut() .mutate(|s| s.add_sig(&signature))?; diff --git a/core/startos/src/registry/os/version/mod.rs b/core/startos/src/registry/os/version/mod.rs index 414994875..5af407c5c 100644 --- a/core/startos/src/registry/os/version/mod.rs +++ b/core/startos/src/registry/os/version/mod.rs @@ -81,7 +81,7 @@ pub async fn add_version( db.as_index_mut() .as_os_mut() .as_versions_mut() - .upsert(&version, || OsVersionInfo::default())? + .upsert(&version, || Ok(OsVersionInfo::default()))? .mutate(|i| { i.headline = headline; i.release_notes = release_notes; diff --git a/core/startos/src/s9pk/merkle_archive/directory_contents.rs b/core/startos/src/s9pk/merkle_archive/directory_contents.rs index 19821cf32..ebc46a60b 100644 --- a/core/startos/src/s9pk/merkle_archive/directory_contents.rs +++ b/core/startos/src/s9pk/merkle_archive/directory_contents.rs @@ -275,9 +275,7 @@ impl DirectoryContents { _ => std::cmp::Ordering::Equal, }) { varint::serialize_varstring(&**name, w).await?; - if let Some(pos) = entry.serialize_header(queue.add(entry).await?, w).await? { - eprintln!("DEBUG ====> {name} @ {pos}"); - } + entry.serialize_header(queue.add(entry).await?, w).await?; } Ok(()) diff --git a/core/startos/src/service/cli.rs b/core/startos/src/service/cli.rs index 82c63d6a7..87491932b 100644 --- a/core/startos/src/service/cli.rs +++ b/core/startos/src/service/cli.rs @@ -29,7 +29,7 @@ impl ContainerCliContext { Self(Arc::new(ContainerCliSeed { socket: cfg .socket - .unwrap_or_else(|| Path::new("/").join(HOST_RPC_SERVER_SOCKET)), + .unwrap_or_else(|| Path::new("/media/startos/rpc").join(HOST_RPC_SERVER_SOCKET)), runtime: OnceCell::new(), })) } diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index c5023476a..0bd31dc9b 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -13,7 +13,7 @@ use imbl::OrdMap; use imbl_value::{json, InternedString}; use itertools::Itertools; use models::{ - ActionId, DataUrl, HealthCheckId, HostId, Id, ImageId, PackageId, ServiceInterfaceId, VolumeId, + ActionId, DataUrl, HealthCheckId, HostId, ImageId, PackageId, ServiceInterfaceId, VolumeId, }; use patch_db::json_ptr::JsonPointer; use rpc_toolkit::{from_fn, from_fn_async, Context, Empty, HandlerExt, ParentHandler}; @@ -29,6 +29,7 @@ use crate::db::model::package::{ use crate::disk::mount::filesystem::idmapped::IdMapped; use crate::disk::mount::filesystem::loop_dev::LoopDev; use crate::disk::mount::filesystem::overlayfs::OverlayGuard; +use crate::net::host::address::HostAddress; use crate::net::host::binding::BindOptions; use crate::net::host::{self, HostKind}; use crate::net::service_interface::{ @@ -169,6 +170,7 @@ pub fn service_effect_handler() -> ParentHandler { .no_display() .with_call_remote::(), ) + .subcommand("setSystemSmtp", from_fn_async(set_system_smtp).no_cli()) .subcommand("getSystemSmtp", from_fn_async(get_system_smtp).no_cli()) .subcommand("getContainerIp", from_fn_async(get_container_ip).no_cli()) .subcommand( @@ -206,6 +208,12 @@ struct GetSystemSmtpParams { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] +struct SetSystemSmtpParams { + smtp: String, +} +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] +#[ts(export)] +#[serde(rename_all = "camelCase")] struct GetServicePortForwardParams { #[ts(type = "string | null")] package_id: Option, @@ -236,6 +244,7 @@ struct GetPrimaryUrlParams { package_id: Option, service_interface_id: String, callback: Callback, + host_id: HostId, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] @@ -249,7 +258,7 @@ struct ListServiceInterfacesParams { #[ts(export)] #[serde(rename_all = "camelCase")] struct RemoveAddressParams { - id: String, + id: ServiceInterfaceId, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] @@ -316,11 +325,39 @@ struct MountParams { location: String, target: MountTarget, } +async fn set_system_smtp(context: EffectContext, data: SetSystemSmtpParams) -> Result<(), Error> { + let context = context.deref()?; + context + .ctx + .db + .mutate(|db| { + let model = db.as_public_mut().as_server_info_mut().as_smtp_mut(); + model.ser(&mut Some(data.smtp)) + }) + .await +} async fn get_system_smtp( context: EffectContext, data: GetSystemSmtpParams, -) -> Result { - todo!() +) -> Result { + let context = context.deref()?; + let res = context + .ctx + .db + .peek() + .await + .into_public() + .into_server_info() + .into_smtp() + .de()?; + + match res { + Some(smtp) => Ok(smtp), + None => Err(Error::new( + eyre!("SMTP not found"), + crate::ErrorKind::NotFound, + )), + } } async fn get_container_ip(context: EffectContext, _: Empty) -> Result { let context = context.deref()?; @@ -337,8 +374,24 @@ async fn get_service_port_forward( let net_service = context.persistent_container.net_service.lock().await; net_service.get_ext_port(data.host_id, internal_port) } -async fn clear_network_interfaces(context: EffectContext, _: Empty) -> Result { - todo!() +async fn clear_network_interfaces(context: EffectContext, _: Empty) -> Result<(), Error> { + let context = context.deref()?; + let package_id = context.id.clone(); + + context + .ctx + .db + .mutate(|db| { + let model = db + .as_public_mut() + .as_package_data_mut() + .as_idx_mut(&package_id) + .or_not_found(&package_id)? + .as_service_interfaces_mut(); + let mut new_map = BTreeMap::new(); + model.ser(&mut new_map) + }) + .await } async fn export_service_interface( context: EffectContext, @@ -397,8 +450,27 @@ async fn export_service_interface( async fn get_primary_url( context: EffectContext, data: GetPrimaryUrlParams, -) -> Result { - todo!() +) -> Result { + let context = context.deref()?; + let package_id = context.id.clone(); + + let db_model = context.ctx.db.peek().await; + + let pkg_data_model = db_model + .as_public() + .as_package_data() + .as_idx(&package_id) + .or_not_found(&package_id)?; + + let host = pkg_data_model.de()?.hosts.get_host_primary(&data.host_id); + + match host { + Some(host_address) => Ok(host_address), + None => Err(Error::new( + eyre!("Primary Url not found for {}", data.host_id), + crate::ErrorKind::NotFound, + )), + } } async fn list_service_interfaces( context: EffectContext, @@ -419,9 +491,24 @@ async fn list_service_interfaces( .into_service_interfaces() .de() } +async fn remove_address(context: EffectContext, data: RemoveAddressParams) -> Result<(), Error> { + let context = context.deref()?; + let package_id = context.id.clone(); -async fn remove_address(context: EffectContext, data: RemoveAddressParams) -> Result { - todo!() + context + .ctx + .db + .mutate(|db| { + let model = db + .as_public_mut() + .as_package_data_mut() + .as_idx_mut(&package_id) + .or_not_found(&package_id)? + .as_service_interfaces_mut(); + model.remove(&data.id) + }) + .await?; + Ok(()) } async fn export_action(context: EffectContext, data: ExportActionParams) -> Result<(), Error> { let context = context.deref()?; @@ -607,7 +694,7 @@ fn chroot( cmd.env(k, v); } } - nix::unistd::setsid().with_kind(ErrorKind::Lxc)?; // TODO: error code + nix::unistd::setsid().ok(); // https://stackoverflow.com/questions/25701333/os-setsid-operation-not-permitted std::os::unix::fs::chroot(path)?; if let Some(uid) = user.as_deref().and_then(|u| u.parse::().ok()) { cmd.uid(uid); @@ -735,7 +822,7 @@ async fn set_store( let model = db .as_private_mut() .as_package_stores_mut() - .upsert(&package_id, || json!({}))?; + .upsert(&package_id, || Ok(json!({})))?; let mut model_value = model.de()?; if model_value.is_null() { model_value = json!({}); @@ -1053,7 +1140,7 @@ pub async fn create_overlayed_image( .s9pk .as_archive() .contents() - .get_path(dbg!(&path)) + .get_path(&path) .and_then(|e| e.as_file()) { let guid = new_guid(); diff --git a/debian/postinst b/debian/postinst index 4e8350376..238bd9457 100755 --- a/debian/postinst +++ b/debian/postinst @@ -81,10 +81,6 @@ sed -i '/^\s*#\?\s*issue_discards\s*=\s*/c\issue_discards = 1' /etc/lvm/lvm.conf mkdir -p /etc/nginx/ssl -# fix to suppress docker warning, fixed in 21.xx release of docker cli: https://github.com/docker/cli/pull/2934 -mkdir -p /root/.docker -touch /root/.docker/config.json - cat << EOF > /etc/tor/torrc SocksPort 0.0.0.0:9050 SocksPolicy accept 127.0.0.1 diff --git a/image-recipe/build.sh b/image-recipe/build.sh index 9f62000ce..5ec500ce3 100755 --- a/image-recipe/build.sh +++ b/image-recipe/build.sh @@ -166,12 +166,6 @@ fi curl -fsSL https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc > config/archives/tor.key echo "deb [arch=${IB_TARGET_ARCH} signed-by=/etc/apt/trusted.gpg.d/tor.key.gpg] https://deb.torproject.org/torproject.org ${IB_SUITE} main" > config/archives/tor.list -curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o config/archives/docker.key -echo "deb [arch=${IB_TARGET_ARCH} signed-by=/etc/apt/trusted.gpg.d/docker.key.gpg] https://download.docker.com/linux/debian ${IB_SUITE} stable" > config/archives/docker.list - -curl -fsSL https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/Debian_Testing/Release.key | gpg --dearmor -o config/archives/podman.key -echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/trusted.gpg.d/podman.key.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/Debian_Testing/ /" > config/archives/podman.list - # Dependencies ## Base dependencies diff --git a/sdk/lib/StartSdk.ts b/sdk/lib/StartSdk.ts index 4d7514d88..f2eabe6be 100644 --- a/sdk/lib/StartSdk.ts +++ b/sdk/lib/StartSdk.ts @@ -28,7 +28,7 @@ import { DependencyConfig, Update } from "./dependencies/DependencyConfig" import { BackupSet, Backups } from "./backup/Backups" import { smtpConfig } from "./config/configConstants" import { Daemons } from "./mainFn/Daemons" -import { healthCheck } from "./health/HealthCheck" +import { healthCheck, HealthCheckParams } from "./health/HealthCheck" import { checkPortListening } from "./health/checkFns/checkPortListening" import { checkWebUrl, runHealthScript } from "./health/checkFns" import { List } from "./config/builder/list" @@ -78,6 +78,7 @@ import { Checker, EmVer } from "./emverLite/mod" import { ExposedStorePaths } from "./store/setupExposeStore" import { PathBuilder, extractJsonPath, pathBuilder } from "./store/PathBuilder" import { checkAllDependencies } from "./dependencies/dependencies" +import { health } from "." // prettier-ignore type AnyNeverCond = @@ -186,13 +187,13 @@ export class StartSdk { nullIfEmpty, runCommand: async ( effects: Effects, - imageId: Manifest["images"][number], + image: { id: Manifest["images"][number]; sharedRun?: boolean }, command: ValidIfNoStupidEscape | [string, ...string[]], options: CommandOptions & { mounts?: { path: string; options: MountOptions }[] }, ): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> => { - return runCommand(effects, imageId, command, options) + return runCommand(effects, image, command, options) }, createAction: < @@ -264,7 +265,9 @@ export class StartSdk { ) }, HealthCheck: { - of: healthCheck, + of(o: HealthCheckParams) { + return healthCheck(o) + }, }, Dependency: { of(data: Dependency["data"]) { @@ -740,14 +743,14 @@ export class StartSdk { export async function runCommand( effects: Effects, - imageId: Manifest["images"][number], + image: { id: Manifest["images"][number]; sharedRun?: boolean }, command: string | [string, ...string[]], options: CommandOptions & { mounts?: { path: string; options: MountOptions }[] }, ): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> { const commands = splitCommand(command) - const overlay = await Overlay.of(effects, imageId) + const overlay = await Overlay.of(effects, image) try { for (let mount of options.mounts || []) { await overlay.mount(mount.options, mount.path) diff --git a/sdk/lib/health/HealthCheck.ts b/sdk/lib/health/HealthCheck.ts index d7bbc2f44..ca0e3ebb9 100644 --- a/sdk/lib/health/HealthCheck.ts +++ b/sdk/lib/health/HealthCheck.ts @@ -1,5 +1,5 @@ import { InterfaceReceipt } from "../interfaces/interfaceReceipt" -import { Daemon, Effects } from "../types" +import { Daemon, Effects, SDKManifest } from "../types" import { CheckResult } from "./checkFns/CheckResult" import { HealthReceipt } from "./HealthReceipt" import { Trigger } from "../trigger" @@ -9,16 +9,23 @@ import { once } from "../util/once" import { Overlay } from "../util/Overlay" import { object, unknown } from "ts-matches" -export function healthCheck(o: { +export type HealthCheckParams = { effects: Effects name: string - imageId: string + image: { + id: Manifest["images"][number] + sharedRun?: boolean + } trigger?: Trigger fn(overlay: Overlay): Promise | CheckResult onFirstSuccess?: () => unknown | Promise -}) { +} + +export function healthCheck( + o: HealthCheckParams, +) { new Promise(async () => { - const overlay = await Overlay.of(o.effects, o.imageId) + const overlay = await Overlay.of(o.effects, o.image) try { let currentValue: TriggerInput = { hadSuccess: false, diff --git a/sdk/lib/mainFn/Daemons.ts b/sdk/lib/mainFn/Daemons.ts index 01fd7f2c7..efa1a2484 100644 --- a/sdk/lib/mainFn/Daemons.ts +++ b/sdk/lib/mainFn/Daemons.ts @@ -23,7 +23,7 @@ type Daemon< > = { id: "" extends Id ? never : Id command: ValidIfNoStupidEscape | [string, ...string[]] - imageId: Manifest["images"][number] + image: { id: Manifest["images"][number]; sharedRun?: boolean } mounts: Mounts env?: Record ready: { @@ -40,7 +40,7 @@ export const runDaemon = () => async ( effects: Effects, - imageId: Manifest["images"][number], + image: { id: Manifest["images"][number]; sharedRun?: boolean }, command: ValidIfNoStupidEscape | [string, ...string[]], options: CommandOptions & { mounts?: { path: string; options: MountOptions }[] @@ -48,7 +48,7 @@ export const runDaemon = }, ): Promise => { const commands = splitCommand(command) - const overlay = options.overlay || (await Overlay.of(effects, imageId)) + const overlay = options.overlay || (await Overlay.of(effects, image)) for (let mount of options.mounts || []) { await overlay.mount(mount.options, mount.path) } @@ -183,9 +183,9 @@ export class Daemons { daemon.requires?.map((id) => daemonsStarted[id]) ?? [], ) daemonsStarted[daemon.id] = requiredPromise.then(async () => { - const { command, imageId } = daemon + const { command, image } = daemon - const child = runDaemon()(effects, imageId, command, { + const child = runDaemon()(effects, image, command, { env: daemon.env, mounts: daemon.mounts.build(), }) diff --git a/sdk/lib/osBindings/GetPrimaryUrlParams.ts b/sdk/lib/osBindings/GetPrimaryUrlParams.ts index d9394f4ce..1a68ecc7b 100644 --- a/sdk/lib/osBindings/GetPrimaryUrlParams.ts +++ b/sdk/lib/osBindings/GetPrimaryUrlParams.ts @@ -1,8 +1,10 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Callback } from "./Callback" +import type { HostId } from "./HostId" export type GetPrimaryUrlParams = { packageId: string | null serviceInterfaceId: string callback: Callback + hostId: HostId } diff --git a/sdk/lib/osBindings/RemoveAddressParams.ts b/sdk/lib/osBindings/RemoveAddressParams.ts index bdc781837..14099ebbc 100644 --- a/sdk/lib/osBindings/RemoveAddressParams.ts +++ b/sdk/lib/osBindings/RemoveAddressParams.ts @@ -1,3 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ServiceInterfaceId } from "./ServiceInterfaceId" -export type RemoveAddressParams = { id: string } +export type RemoveAddressParams = { id: ServiceInterfaceId } diff --git a/sdk/lib/osBindings/ServerInfo.ts b/sdk/lib/osBindings/ServerInfo.ts index 284eba336..935e3a99f 100644 --- a/sdk/lib/osBindings/ServerInfo.ts +++ b/sdk/lib/osBindings/ServerInfo.ts @@ -28,4 +28,5 @@ export type ServerInfo = { ntpSynced: boolean zram: boolean governor: Governor | null + smtp: string | null } diff --git a/sdk/lib/osBindings/SetSystemSmtpParams.ts b/sdk/lib/osBindings/SetSystemSmtpParams.ts new file mode 100644 index 000000000..49c66e86c --- /dev/null +++ b/sdk/lib/osBindings/SetSystemSmtpParams.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SetSystemSmtpParams = { smtp: string } diff --git a/sdk/lib/osBindings/index.ts b/sdk/lib/osBindings/index.ts index d14a9fb87..4fcb9a617 100644 --- a/sdk/lib/osBindings/index.ts +++ b/sdk/lib/osBindings/index.ts @@ -113,6 +113,7 @@ export { SetDependenciesParams } from "./SetDependenciesParams" export { SetHealth } from "./SetHealth" export { SetMainStatus } from "./SetMainStatus" export { SetStoreParams } from "./SetStoreParams" +export { SetSystemSmtpParams } from "./SetSystemSmtpParams" export { SignAssetParams } from "./SignAssetParams" export { SignatureInfo } from "./SignatureInfo" export { Signature } from "./Signature" diff --git a/sdk/lib/util/Overlay.ts b/sdk/lib/util/Overlay.ts index 0e2dec58e..794b78732 100644 --- a/sdk/lib/util/Overlay.ts +++ b/sdk/lib/util/Overlay.ts @@ -12,10 +12,19 @@ export class Overlay { readonly rootfs: string, readonly guid: string, ) {} - static async of(effects: T.Effects, imageId: string) { + static async of( + effects: T.Effects, + image: { id: string; sharedRun?: boolean }, + ) { + const { id: imageId, sharedRun } = image const [rootfs, guid] = await effects.createOverlayedImage({ imageId }) - for (const dirPart of ["dev", "sys", "proc", "run"] as const) { + const shared = ["dev", "sys", "proc"] + if (!!sharedRun) { + shared.push("run") + } + + for (const dirPart of shared) { await fs.mkdir(`${rootfs}/${dirPart}`, { recursive: true }) await execFile("mount", [ "--rbind", diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts index 9f17c22eb..a4ed61278 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -74,6 +74,7 @@ export const mockPatchData: DataModel = { platform: 'x86_64-nonfree', zram: true, governor: 'performance', + smtp: 'todo', wifi: { interface: 'wlan0', ssids: [],