mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
More svc effect handlers (#2610)
* complete get_primary_url fn * complete clear_network_interfaces fn * formatting * complete remove_address fn * get_system_smtp wip * complete get_system_smtp and set_system_smtp * add SetSystemSmtpParams struct * add set_system_smtp subcommand * Remove 'Copy' implementation from `HostAddress` Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> * Refactor `get_host_primary` fn and clone resulting `HostAddress` Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> * misc fixes and debug info * seed hosts with a tor address --------- Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
2
Makefile
2
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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<InternedString> for Id {
|
||||
type Error = InvalidId;
|
||||
fn try_from(value: InternedString) -> Result<Self, Self::Error> {
|
||||
|
||||
@@ -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<Governor>,
|
||||
pub smtp: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel, TS)]
|
||||
|
||||
@@ -220,7 +220,7 @@ where
|
||||
}
|
||||
pub fn upsert<F>(&mut self, key: &T::Key, value: F) -> Result<&mut Model<T::Value>, Error>
|
||||
where
|
||||
F: FnOnce() -> T::Value,
|
||||
F: FnOnce() -> Result<T::Value, Error>,
|
||||
{
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -348,6 +348,12 @@ pub async fn init(cfg: &ServerConfig) -> Result<InitResult, Error> {
|
||||
})
|
||||
.await?;
|
||||
|
||||
Command::new("systemctl")
|
||||
.arg("start")
|
||||
.arg("lxc-net.service")
|
||||
.invoke(ErrorKind::Lxc)
|
||||
.await?;
|
||||
|
||||
crate::version::init(&db).await?;
|
||||
|
||||
db.mutate(|d| {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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<HostInfo> {
|
||||
pub fn host_for<'a>(
|
||||
db: &'a mut DatabaseModel,
|
||||
package_id: &PackageId,
|
||||
host_id: &HostId,
|
||||
host_kind: HostKind,
|
||||
) -> Result<&'a mut Model<Host>, Error> {
|
||||
fn host_info<'a>(
|
||||
db: &'a mut DatabaseModel,
|
||||
package_id: &PackageId,
|
||||
) -> Result<&'a mut Model<HostInfo>, 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<Host> {
|
||||
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<HostAddress> {
|
||||
self.0.get(&host_id).and_then(|h| h.primary.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<u16, (u16, Option<AddSslOptions>, Arc<()>)>,
|
||||
tor: BTreeMap<OnionAddressV3, (OrdMap<u16, SocketAddr>, Vec<Arc<()>>)>,
|
||||
@@ -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) {
|
||||
|
||||
@@ -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))?;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -275,9 +275,7 @@ impl<S: FileSource> DirectoryContents<S> {
|
||||
_ => 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(())
|
||||
|
||||
@@ -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(),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -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<C: Context>() -> ParentHandler<C> {
|
||||
.no_display()
|
||||
.with_call_remote::<ContainerCliContext>(),
|
||||
)
|
||||
.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<PackageId>,
|
||||
@@ -236,6 +244,7 @@ struct GetPrimaryUrlParams {
|
||||
package_id: Option<PackageId>,
|
||||
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<Value, Error> {
|
||||
todo!()
|
||||
) -> Result<String, Error> {
|
||||
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<Ipv4Addr, Error> {
|
||||
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<Value, Error> {
|
||||
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<Value, Error> {
|
||||
todo!()
|
||||
) -> Result<HostAddress, Error> {
|
||||
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<Value, Error> {
|
||||
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<C: Context>(
|
||||
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::<u32>().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();
|
||||
|
||||
4
debian/postinst
vendored
4
debian/postinst
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<T extends any[], Then, Else> =
|
||||
@@ -186,13 +187,13 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
|
||||
nullIfEmpty,
|
||||
runCommand: async <A extends string>(
|
||||
effects: Effects,
|
||||
imageId: Manifest["images"][number],
|
||||
image: { id: Manifest["images"][number]; sharedRun?: boolean },
|
||||
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
||||
options: CommandOptions & {
|
||||
mounts?: { path: string; options: MountOptions }[]
|
||||
},
|
||||
): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> => {
|
||||
return runCommand<Manifest>(effects, imageId, command, options)
|
||||
return runCommand<Manifest>(effects, image, command, options)
|
||||
},
|
||||
|
||||
createAction: <
|
||||
@@ -264,7 +265,9 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
|
||||
)
|
||||
},
|
||||
HealthCheck: {
|
||||
of: healthCheck,
|
||||
of(o: HealthCheckParams<Manifest>) {
|
||||
return healthCheck<Manifest>(o)
|
||||
},
|
||||
},
|
||||
Dependency: {
|
||||
of(data: Dependency["data"]) {
|
||||
@@ -740,14 +743,14 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
|
||||
|
||||
export async function runCommand<Manifest extends SDKManifest>(
|
||||
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)
|
||||
|
||||
@@ -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<Manifest extends SDKManifest> = {
|
||||
effects: Effects
|
||||
name: string
|
||||
imageId: string
|
||||
image: {
|
||||
id: Manifest["images"][number]
|
||||
sharedRun?: boolean
|
||||
}
|
||||
trigger?: Trigger
|
||||
fn(overlay: Overlay): Promise<CheckResult> | CheckResult
|
||||
onFirstSuccess?: () => unknown | Promise<unknown>
|
||||
}) {
|
||||
}
|
||||
|
||||
export function healthCheck<Manifest extends SDKManifest>(
|
||||
o: HealthCheckParams<Manifest>,
|
||||
) {
|
||||
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,
|
||||
|
||||
@@ -23,7 +23,7 @@ type Daemon<
|
||||
> = {
|
||||
id: "" extends Id ? never : Id
|
||||
command: ValidIfNoStupidEscape<Command> | [string, ...string[]]
|
||||
imageId: Manifest["images"][number]
|
||||
image: { id: Manifest["images"][number]; sharedRun?: boolean }
|
||||
mounts: Mounts<Manifest>
|
||||
env?: Record<string, string>
|
||||
ready: {
|
||||
@@ -40,7 +40,7 @@ export const runDaemon =
|
||||
<Manifest extends SDKManifest>() =>
|
||||
async <A extends string>(
|
||||
effects: Effects,
|
||||
imageId: Manifest["images"][number],
|
||||
image: { id: Manifest["images"][number]; sharedRun?: boolean },
|
||||
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
||||
options: CommandOptions & {
|
||||
mounts?: { path: string; options: MountOptions }[]
|
||||
@@ -48,7 +48,7 @@ export const runDaemon =
|
||||
},
|
||||
): Promise<DaemonReturned> => {
|
||||
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<Manifest extends SDKManifest, Ids extends string> {
|
||||
daemon.requires?.map((id) => daemonsStarted[id]) ?? [],
|
||||
)
|
||||
daemonsStarted[daemon.id] = requiredPromise.then(async () => {
|
||||
const { command, imageId } = daemon
|
||||
const { command, image } = daemon
|
||||
|
||||
const child = runDaemon<Manifest>()(effects, imageId, command, {
|
||||
const child = runDaemon<Manifest>()(effects, image, command, {
|
||||
env: daemon.env,
|
||||
mounts: daemon.mounts.build(),
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -28,4 +28,5 @@ export type ServerInfo = {
|
||||
ntpSynced: boolean
|
||||
zram: boolean
|
||||
governor: Governor | null
|
||||
smtp: string | null
|
||||
}
|
||||
|
||||
3
sdk/lib/osBindings/SetSystemSmtpParams.ts
Normal file
3
sdk/lib/osBindings/SetSystemSmtpParams.ts
Normal file
@@ -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 }
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -74,6 +74,7 @@ export const mockPatchData: DataModel = {
|
||||
platform: 'x86_64-nonfree',
|
||||
zram: true,
|
||||
governor: 'performance',
|
||||
smtp: 'todo',
|
||||
wifi: {
|
||||
interface: 'wlan0',
|
||||
ssids: [],
|
||||
|
||||
Reference in New Issue
Block a user