From 3c6c0b253de875e121a6959a5ce722538d5c62af Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Mon, 18 Mar 2024 15:14:36 -0600 Subject: [PATCH] move mounts to daemons constructor --- sdk/lib/StartSdk.ts | 2 - sdk/lib/dependency/mountDependencies.ts | 43 ------- sdk/lib/dependency/setupDependencyMounts.ts | 72 ----------- sdk/lib/mainFn/Daemons.ts | 7 +- sdk/lib/mainFn/Mounts.ts | 126 ++++++++++++++++++++ sdk/lib/test/mountDependencies.test.ts | 125 ------------------- sdk/lib/types.ts | 2 +- sdk/lib/util/Overlay.ts | 19 ++- sdk/lib/util/utils.ts | 29 ----- 9 files changed, 149 insertions(+), 276 deletions(-) delete mode 100644 sdk/lib/dependency/mountDependencies.ts delete mode 100644 sdk/lib/dependency/setupDependencyMounts.ts create mode 100644 sdk/lib/mainFn/Mounts.ts delete mode 100644 sdk/lib/test/mountDependencies.test.ts diff --git a/sdk/lib/StartSdk.ts b/sdk/lib/StartSdk.ts index 85f157aa9..938de5fbb 100644 --- a/sdk/lib/StartSdk.ts +++ b/sdk/lib/StartSdk.ts @@ -46,7 +46,6 @@ import { setupMain } from "./mainFn" import { defaultTrigger } from "./trigger/defaultTrigger" import { changeOnFirstSuccess, cooldownTrigger } from "./trigger" import setupConfig, { Read, Save } from "./config/setupConfig" -import { setupDependencyMounts } from "./dependency/setupDependencyMounts" import { InterfacesReceipt, SetInterfaces, @@ -170,7 +169,6 @@ export class StartSdk { }, ) => setupDependencyConfig(config, autoConfigs), setupExports: (fn: SetupExports) => fn, - setupDependencyMounts, setupInit: ( migrations: Migrations, install: Install, diff --git a/sdk/lib/dependency/mountDependencies.ts b/sdk/lib/dependency/mountDependencies.ts deleted file mode 100644 index 31f9cdadc..000000000 --- a/sdk/lib/dependency/mountDependencies.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Effects } from "../types" -import { _ } from "../util" -import { - Path, - ManifestId, - VolumeName, - NamedPath, - matchPath, -} from "./setupDependencyMounts" - -export type MountDependenciesOut = _< - // prettier-ignore - A extends Path ? string : A extends Record ? { - [P in keyof A]: MountDependenciesOut; - } : never -> -export async function mountDependencies< - In extends - | Record>> - | Record> - | Record - | Path, ->(effects: Effects, value: In): Promise> { - if (matchPath.test(value)) { - const mountPath = `${value.manifestId}/${value.volume}/${value.name}` - - return (await effects.mount({ - location: mountPath, - target: { - packageId: value.manifestId, - path: value.path, - readonly: value.readonly, - volumeId: value.volume, - }, - })) as MountDependenciesOut - } - return Object.fromEntries( - Object.entries(value).map(([key, value]) => [ - key, - mountDependencies(effects, value), - ]), - ) as Record as MountDependenciesOut -} diff --git a/sdk/lib/dependency/setupDependencyMounts.ts b/sdk/lib/dependency/setupDependencyMounts.ts deleted file mode 100644 index 15e2ca1a3..000000000 --- a/sdk/lib/dependency/setupDependencyMounts.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { boolean, object, string } from "ts-matches" -import { SDKManifest } from "../manifest/ManifestTypes" -import { deepMerge } from "../util/deepMerge" - -export type VolumeName = string -export type NamedPath = string -export type ManifestId = string - -export const matchPath = object({ - name: string, - volume: string, - path: string, - manifestId: string, - readonly: boolean, -}) -export type Path = typeof matchPath._TYPE -export type BuildPath< - ManifestId extends string, - VolumeId extends string, - PathName extends string, - Value extends Path, -> = { - [PId in ManifestId]: { - [V in VolumeId]: { - [N in PathName]: Value - } - } -} -class SetupDependencyMounts { - private constructor(readonly building: Building) {} - - static of() { - return new SetupDependencyMounts({}) - } - - addPath< - Name extends string, - Volume extends M["volumes"][0] & string, - Path extends string, - ManifestId extends M["id"], - M extends SDKManifest, - >(addPath: { - name: Name - volume: Volume - path: Path - manifest: M - readonly: boolean - }) { - const { manifest, ...restPath } = addPath - const newPath = { - ...restPath, - manifestId: manifest.id as ManifestId, - } as const - type NewBuilding = Building & - BuildPath - const building = deepMerge(this.building, { - [newPath.manifestId]: { - [newPath.volume]: { - [newPath.name]: newPath, - }, - }, - }) as NewBuilding - return new SetupDependencyMounts(building) - } - build() { - return this.building - } -} - -export function setupDependencyMounts() { - return SetupDependencyMounts.of() -} diff --git a/sdk/lib/mainFn/Daemons.ts b/sdk/lib/mainFn/Daemons.ts index 368c15c89..442ef7c46 100644 --- a/sdk/lib/mainFn/Daemons.ts +++ b/sdk/lib/mainFn/Daemons.ts @@ -7,6 +7,7 @@ import { defaultTrigger } from "../trigger/defaultTrigger" import { DaemonReturned, Effects, ValidIfNoStupidEscape } from "../types" import { createUtils } from "../util" import { Signals } from "../util/utils" +import { Mounts } from "./Mounts" type Daemon< Manifest extends SDKManifest, Ids extends string, @@ -16,6 +17,7 @@ type Daemon< id: "" extends Id ? never : Id command: ValidIfNoStupidEscape | [string, ...string[]] imageId: Manifest["images"][number] + mounts: Mounts env?: Record ready: { display: string | null @@ -106,7 +108,10 @@ export class Daemons { const { command, imageId } = daemon const utils = createUtils(effects) - const child = utils.runDaemon(imageId, command, { env: daemon.env }) + const child = utils.runDaemon(imageId, command, { + env: daemon.env, + mounts: daemon.mounts.build(), + }) let currentInput: TriggerInput = {} const getCurrentInput = () => currentInput const trigger = (daemon.ready.trigger ?? defaultTrigger)( diff --git a/sdk/lib/mainFn/Mounts.ts b/sdk/lib/mainFn/Mounts.ts new file mode 100644 index 000000000..eeedc79c6 --- /dev/null +++ b/sdk/lib/mainFn/Mounts.ts @@ -0,0 +1,126 @@ +import { SDKManifest } from "../manifest/ManifestTypes" +import { Effects } from "../types" +import { MountOptions } from "../util/Overlay" + +type MountArray = { path: string; options: MountOptions }[] + +export class Mounts { + private constructor( + readonly volumes: { + id: Manifest["volumes"][number] + subpath: string | null + mountpoint: string + readonly: boolean + }[], + readonly assets: { + id: Manifest["assets"][number] + subpath: string | null + mountpoint: string + }[], + readonly dependencies: { + dependencyId: string + volumeId: string + subpath: string | null + mountpoint: string + readonly: boolean + }[], + ) {} + + static of() { + return new Mounts([], [], []) + } + + addVolume( + id: Manifest["volumes"][number], + subpath: string | null, + mountpoint: string, + readonly: boolean, + ) { + this.volumes.push({ + id, + subpath, + mountpoint, + readonly, + }) + return this + } + + addAssets( + id: Manifest["assets"][number], + subpath: string | null, + mountpoint: string, + ) { + this.assets.push({ + id, + subpath, + mountpoint, + }) + return this + } + + addDependency( + dependencyId: keyof Manifest["dependencies"] & string, + volumeId: DependencyManifest["volumes"][number], + subpath: string | null, + mountpoint: string, + readonly: boolean, + ) { + this.dependencies.push({ + dependencyId, + volumeId, + subpath, + mountpoint, + readonly, + }) + return this + } + + build(): MountArray { + const mountpoints = new Set() + for (let mountpoint of this.volumes + .map((v) => v.mountpoint) + .concat(this.assets.map((a) => a.mountpoint)) + .concat(this.dependencies.map((d) => d.mountpoint))) { + if (mountpoints.has(mountpoint)) { + throw new Error( + `cannot mount more than once to mountpoint ${mountpoint}`, + ) + } + mountpoints.add(mountpoint) + } + return ([] as MountArray) + .concat( + this.volumes.map((v) => ({ + path: v.mountpoint, + options: { + type: "volume", + id: v.id, + subpath: v.subpath, + readonly: v.readonly, + }, + })), + ) + .concat( + this.assets.map((a) => ({ + path: a.mountpoint, + options: { + type: "assets", + id: a.id, + subpath: a.subpath, + }, + })), + ) + .concat( + this.dependencies.map((d) => ({ + path: d.mountpoint, + options: { + type: "pointer", + packageId: d.dependencyId, + volumeId: d.volumeId, + subpath: d.subpath, + readonly: d.readonly, + }, + })), + ) + } +} diff --git a/sdk/lib/test/mountDependencies.test.ts b/sdk/lib/test/mountDependencies.test.ts deleted file mode 100644 index 84e76aa54..000000000 --- a/sdk/lib/test/mountDependencies.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { setupManifest } from "../manifest/setupManifest" -import { mountDependencies } from "../dependency/mountDependencies" -import { - BuildPath, - setupDependencyMounts, -} from "../dependency/setupDependencyMounts" - -describe("mountDependencies", () => { - const clnManifest = setupManifest({ - id: "cln", - title: "", - version: "1", - releaseNotes: "", - license: "", - replaces: [], - wrapperRepo: "", - upstreamRepo: "", - supportSite: "", - marketingSite: "", - donationUrl: null, - description: { - short: "", - long: "", - }, - assets: [], - images: [], - volumes: ["main"], - alerts: { - install: null, - update: null, - uninstall: null, - restore: null, - start: null, - stop: null, - }, - dependencies: {}, - }) - const clnManifestVolumes = clnManifest.volumes - const lndManifest = setupManifest({ - id: "lnd", - title: "", - version: "1", - releaseNotes: "", - license: "", - replaces: [], - wrapperRepo: "", - upstreamRepo: "", - supportSite: "", - marketingSite: "", - donationUrl: null, - description: { - short: "", - long: "", - }, - assets: [], - images: [], - volumes: ["main2"], - alerts: { - install: null, - update: null, - uninstall: null, - restore: null, - start: null, - stop: null, - }, - dependencies: {}, - }) - clnManifest.id - - test("Types work", () => { - const dependencyMounts = setupDependencyMounts() - .addPath({ - name: "root", - volume: "main", - path: "/", - manifest: clnManifest, - readonly: true, - }) - .addPath({ - name: "root", - manifest: lndManifest, - volume: "main2", - path: "/", - readonly: true, - }) - .addPath({ - name: "root", - manifest: lndManifest, - // @ts-expect-error Expect that main will throw because it is not in the thing - volume: "main", - path: "/", - readonly: true, - }) - .build() - ;() => { - const test = mountDependencies( - null as any, - dependencyMounts, - ) satisfies Promise<{ - cln: { - main: { - root: string - } - } - lnd: { - main2: { - root: string - } - } - }> - const test2 = mountDependencies( - null as any, - dependencyMounts.cln, - ) satisfies Promise<{ - main: { root: string } - }> - const test3 = mountDependencies( - null as any, - dependencyMounts.cln.main, - ) satisfies Promise<{ - root: string - }> - } - }) -}) diff --git a/sdk/lib/types.ts b/sdk/lib/types.ts index d863fe3d1..d9215cb1b 100644 --- a/sdk/lib/types.ts +++ b/sdk/lib/types.ts @@ -500,7 +500,7 @@ export type Effects = { target: { packageId: string volumeId: string - path: string + subpath: string | null readonly: boolean } }): Promise diff --git a/sdk/lib/util/Overlay.ts b/sdk/lib/util/Overlay.ts index 36dbaead5..7f243d7d1 100644 --- a/sdk/lib/util/Overlay.ts +++ b/sdk/lib/util/Overlay.ts @@ -32,15 +32,25 @@ export class Overlay { ? `${this.rootfs}${path}` : `${this.rootfs}/${path}` if (options.type === "volume") { + const subpath = options.subpath + ? options.subpath.startsWith("/") + ? options.subpath + : `/${options.subpath}` + : "/" await execFile("mount", [ "--bind", - `/media/startos/volumes/${options.id}`, + `/media/startos/volumes/${options.id}${subpath}`, path, ]) } else if (options.type === "assets") { + const subpath = options.subpath + ? options.subpath.startsWith("/") + ? options.subpath + : `/${options.subpath}` + : "/" await execFile("mount", [ "--bind", - `/media/startos/assets/${options.id}`, + `/media/startos/assets/${options.id}${subpath}`, path, ]) } else if (options.type === "pointer") { @@ -140,17 +150,20 @@ export type MountOptions = export type MountOptionsVolume = { type: "volume" id: string + subpath: string | null + readonly: boolean } export type MountOptionsAssets = { type: "assets" id: string + subpath: string | null } export type MountOptionsPointer = { type: "pointer" packageId: string volumeId: string - path: string + subpath: string | null readonly: boolean } diff --git a/sdk/lib/util/utils.ts b/sdk/lib/util/utils.ts index cc18c7714..d56f22305 100644 --- a/sdk/lib/util/utils.ts +++ b/sdk/lib/util/utils.ts @@ -15,16 +15,6 @@ import { } from "../types" import { GetSystemSmtp } from "./GetSystemSmtp" import { GetStore, getStore } from "../store/getStore" -import { - MountDependenciesOut, - mountDependencies, -} from "../dependency/mountDependencies" -import { - ManifestId, - VolumeName, - NamedPath, - Path, -} from "../dependency/setupDependencyMounts" import { MultiHost, Scheme, SingleHost, StaticHost } from "../interfaces/Host" import { ServiceInterfaceBuilder } from "../interfaces/ServiceInterfaceBuilder" import { GetServiceInterface, getServiceInterface } from "./getServiceInterface" @@ -93,15 +83,6 @@ export type Utils< single: (id: string) => SingleHost multi: (id: string) => MultiHost } - mountDependencies: < - In extends - | Record>> - | Record> - | Record - | Path, - >( - value: In, - ) => Promise> serviceInterface: { getOwn: (id: ServiceInterfaceId) => GetServiceInterface & WrapperOverWrite get: (opts: { @@ -306,16 +287,6 @@ export const createUtils = < }, checkPortListening: checkPortListening.bind(null, effects), checkWebUrl: checkWebUrl.bind(null, effects), - - mountDependencies: < - In extends - | Record>> - | Record> - | Record - | Path, - >( - value: In, - ) => mountDependencies(effects, value), } } function noop(): void {}