chore: Update the types and get the container-runtime working

This commit is contained in:
J H
2024-03-20 10:48:03 -06:00
parent 8d83f64aba
commit 53d82618d9
10 changed files with 139 additions and 88 deletions

View File

@@ -28,9 +28,15 @@ export class DockerProcedureContainer {
await fs.mkdir(path, { recursive: true }) await fs.mkdir(path, { recursive: true })
const volumeMount = volumes[mount] const volumeMount = volumes[mount]
if (volumeMount.type === "data") { if (volumeMount.type === "data") {
await overlay.mount({ type: "volume", id: mount }, mounts[mount]) await overlay.mount(
{ type: "volume", id: mount, subpath: null, readonly: false },
mounts[mount],
)
} else if (volumeMount.type === "assets") { } else if (volumeMount.type === "assets") {
await overlay.mount({ type: "assets", id: mount }, mounts[mount]) await overlay.mount(
{ type: "assets", id: mount, subpath: null },
mounts[mount],
)
} else if (volumeMount.type === "certificate") { } else if (volumeMount.type === "certificate") {
volumeMount volumeMount
const certChain = await effects.getSslCertificate({ const certChain = await effects.getSslCertificate({
@@ -56,7 +62,7 @@ export class DockerProcedureContainer {
location: path, location: path,
target: { target: {
packageId: volumeMount["package-id"], packageId: volumeMount["package-id"],
path: volumeMount.path, subpath: volumeMount.path,
readonly: volumeMount.readonly, readonly: volumeMount.readonly,
volumeId: volumeMount["volume-id"], volumeId: volumeMount["volume-id"],
}, },

View File

@@ -2,8 +2,7 @@ import { PolyfillEffects } from "./polyfillEffects"
import { DockerProcedureContainer } from "./DockerProcedureContainer" import { DockerProcedureContainer } from "./DockerProcedureContainer"
import { SystemForEmbassy } from "." import { SystemForEmbassy } from "."
import { HostSystemStartOs } from "../../HostSystemStartOs" import { HostSystemStartOs } from "../../HostSystemStartOs"
import { util, Daemons, types as T } from "@start9labs/start-sdk" import { Daemons, T, daemons } from "@start9labs/start-sdk"
import { exec } from "child_process"
const EMBASSY_HEALTH_INTERVAL = 15 * 1000 const EMBASSY_HEALTH_INTERVAL = 15 * 1000
const EMBASSY_PROPERTIES_LOOP = 30 * 1000 const EMBASSY_PROPERTIES_LOOP = 30 * 1000
@@ -39,7 +38,6 @@ export class MainLoop {
private async constructMainEvent() { private async constructMainEvent() {
const { system, effects } = this const { system, effects } = this
const utils = util.createUtils(effects)
const currentCommand: [string, ...string[]] = [ const currentCommand: [string, ...string[]] = [
system.manifest.main.entrypoint, system.manifest.main.entrypoint,
...system.manifest.main.args, ...system.manifest.main.args,
@@ -67,7 +65,8 @@ export class MainLoop {
// }), // }),
// } // }
} }
const daemon = await utils.runDaemon( const daemon = await daemons.runDaemon()(
this.effects,
this.system.manifest.main.image, this.system.manifest.main.image,
currentCommand, currentCommand,
{ {

View File

@@ -1,4 +1,4 @@
import { types as T, util, EmVer, Utils } from "@start9labs/start-sdk" import { types as T, util, EmVer } from "@start9labs/start-sdk"
import * as fs from "fs/promises" import * as fs from "fs/promises"
import { PolyfillEffects } from "./polyfillEffects" import { PolyfillEffects } from "./polyfillEffects"
@@ -22,6 +22,9 @@ import {
any, any,
tuple, tuple,
number, number,
anyOf,
deferred,
Parser,
} from "ts-matches" } from "ts-matches"
import { HostSystemStartOs } from "../../HostSystemStartOs" import { HostSystemStartOs } from "../../HostSystemStartOs"
import { JsonPath, unNestPath } from "../../../Models/JsonPath" import { JsonPath, unNestPath } from "../../../Models/JsonPath"
@@ -37,58 +40,94 @@ const MANIFEST_LOCATION = "/usr/lib/startos/package/embassyManifest.json"
const EMBASSY_JS_LOCATION = "/usr/lib/startos/package/embassy.js" const EMBASSY_JS_LOCATION = "/usr/lib/startos/package/embassy.js"
const EMBASSY_POINTER_PATH_PREFIX = "/embassyConfig" const EMBASSY_POINTER_PATH_PREFIX = "/embassyConfig"
const matchPackagePropertyObject = object({ export type PackagePropertiesV2 = {
value: any, [name: string]: PackagePropertyObject | PackagePropertyString
type: literal("object"), }
description: string, export type PackagePropertyString = {
}) type: "string"
description?: string
const matchPackagePropertyString = object( value: string
{ /** Let's the ui make this copyable button */
type: literal("string"), copyable?: boolean
/** Let the ui create a qr for this field */
qr?: boolean
/** Hiding the value unless toggled off for field */
masked?: boolean
}
export type PackagePropertyObject = {
value: PackagePropertiesV2
type: "object"
description: string
}
const [matchPackageProperties, setMatchPackageProperties] =
deferred<PackagePropertiesV2>()
const matchPackagePropertyObject: Parser<unknown, PackagePropertyObject> =
object({
value: matchPackageProperties,
type: literal("object"),
description: string, description: string,
value: string, })
copyable: boolean,
qr: boolean, const matchPackagePropertyString: Parser<unknown, PackagePropertyString> =
masked: boolean, object(
}, {
["copyable", "description", "qr", "masked"], type: literal("string"),
description: string,
value: string,
copyable: boolean,
qr: boolean,
masked: boolean,
},
["copyable", "description", "qr", "masked"],
)
setMatchPackageProperties(
dictionary([
string,
anyOf(matchPackagePropertyObject, matchPackagePropertyString),
]),
) )
const matchProperties = object({ const matchProperties = object({
version: literal(2), version: literal(2),
data: any, data: matchPackageProperties,
}) })
type ExportUi = { type ExportUi = {
value: string values: { [key: string]: any }
title: string expose: { [key: string]: T.ExposeUiPathsAll }
description?: string | undefined
masked?: boolean | undefined
copyable?: boolean | undefined
qr?: boolean | undefined
} }
function propertiesToExportUi(properties: unknown): ExportUi[] { function propertiesToExportUi(
if (!object.test(properties)) return [] properties: PackagePropertiesV2,
const paths: ExportUi[] = [] previousPath = "",
for (const key in properties) { ): ExportUi {
const value: unknown = (properties as any)[key] const exportUi: ExportUi = {
if (matchPackagePropertyObject.test(value)) { values: {},
paths.push(...propertiesToExportUi(value)) expose: {},
}
for (const [key, value] of Object.entries(properties)) {
const path = `${previousPath}/${key}`
if (value.type === "object") {
const { values, expose } = propertiesToExportUi(value.value, path)
exportUi.values[key] = values
exportUi.expose[key] = {
type: "object",
value: expose,
description: value.description,
}
continue continue
} }
if (!matchPackagePropertyString.test(value)) continue exportUi.values[key] = value.value
paths.push({ exportUi.expose[key] = {
value: value.value, type: "string",
title: key, path,
description: value.description, description: value.description ?? null,
masked: value.masked, masked: value.masked ?? false,
copyable: value.copyable, copyable: value.copyable ?? null,
qr: value.qr, qr: value.qr ?? null,
}) }
} }
return paths return exportUi
} }
export class SystemForEmbassy implements System { export class SystemForEmbassy implements System {
@@ -455,14 +494,9 @@ export class SystemForEmbassy implements System {
const exposeUis = propertiesToExportUi(properties.data) const exposeUis = propertiesToExportUi(properties.data)
await effects.store.set<any, any>({ await effects.store.set<any, any>({
path: "/properties", path: "/properties",
value: exposeUis.map((x) => x.value), value: exposeUis.values,
})
await effects.exposeUi({
paths: exposeUis.map((x, i) => ({
...x,
path: `/properties/${i}`,
})) as any[],
}) })
await effects.exposeUi(exposeUis.expose)
} else if (setConfigValue.type === "script") { } else if (setConfigValue.type === "script") {
const moduleCode = this.moduleCode const moduleCode = this.moduleCode
const method = moduleCode.properties const method = moduleCode.properties
@@ -479,14 +513,9 @@ export class SystemForEmbassy implements System {
const exposeUis = propertiesToExportUi(properties.data) const exposeUis = propertiesToExportUi(properties.data)
await effects.store.set<any, any>({ await effects.store.set<any, any>({
path: "/properties", path: "/properties",
value: exposeUis.map((x) => x.value), value: exposeUis.values,
})
await effects.exposeUi({
paths: exposeUis.map((x, i) => ({
...x,
path: `/properties/${i}`,
})) as any[],
}) })
await effects.exposeUi(exposeUis.expose)
} }
} }
private async health( private async health(
@@ -991,7 +1020,6 @@ async function updateConfig(
) { ) {
if (!dictionary([string, unknown]).test(spec)) return if (!dictionary([string, unknown]).test(spec)) return
if (!dictionary([string, unknown]).test(mutConfigValue)) return if (!dictionary([string, unknown]).test(mutConfigValue)) return
const utils = util.createUtils(effects)
for (const key in spec) { for (const key in spec) {
const specValue = spec[key] const specValue = spec[key]
@@ -1020,8 +1048,8 @@ async function updateConfig(
if (matchPointerPackage.test(specValue)) { if (matchPointerPackage.test(specValue)) {
if (specValue.target === "tor-key") if (specValue.target === "tor-key")
throw new Error("This service uses an unsupported target TorKey") throw new Error("This service uses an unsupported target TorKey")
const filled = await utils.serviceInterface const filled = await util
.get({ .getServiceInterface(effects, {
packageId: specValue["package-id"], packageId: specValue["package-id"],
id: specValue.interface, id: specValue.interface,
}) })

View File

@@ -3,23 +3,18 @@ import * as oet from "./oldEmbassyTypes"
import { Volume } from "../../../Models/Volume" import { Volume } from "../../../Models/Volume"
import * as child_process from "child_process" import * as child_process from "child_process"
import { promisify } from "util" import { promisify } from "util"
import { util, Utils } from "@start9labs/start-sdk" import { Daemons, startSdk, T } from "@start9labs/start-sdk"
import { Manifest } from "./matchManifest"
import { HostSystemStartOs } from "../../HostSystemStartOs" import { HostSystemStartOs } from "../../HostSystemStartOs"
import "isomorphic-fetch" import "isomorphic-fetch"
import { Manifest } from "./matchManifest"
const { createUtils } = util
const execFile = promisify(child_process.execFile) const execFile = promisify(child_process.execFile)
export class PolyfillEffects implements oet.Effects { export class PolyfillEffects implements oet.Effects {
private utils: Utils<any, any>
constructor( constructor(
readonly effects: HostSystemStartOs, readonly effects: HostSystemStartOs,
private manifest: Manifest, private manifest: Manifest,
) { ) {}
this.utils = createUtils(effects as any)
}
async writeFile(input: { async writeFile(input: {
path: string path: string
volumeId: string volumeId: string
@@ -99,9 +94,14 @@ export class PolyfillEffects implements oet.Effects {
args?: string[] | undefined args?: string[] | undefined
timeoutMillis?: number | undefined timeoutMillis?: number | undefined
}): Promise<oet.ResultType<string>> { }): Promise<oet.ResultType<string>> {
return this.utils return startSdk
.runCommand(this.manifest.main.image, [command, ...(args || [])], {}) .runCommand(
.then((x) => ({ this.effects,
this.manifest.main.image,
[command, ...(args || [])],
{},
)
.then((x: any) => ({
stderr: x.stderr.toString(), stderr: x.stderr.toString(),
stdout: x.stdout.toString(), stdout: x.stdout.toString(),
})) }))

View File

@@ -205,16 +205,7 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
mounts?: { path: string; options: MountOptions }[] mounts?: { path: string; options: MountOptions }[]
}, },
): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> => { ): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> => {
const commands = splitCommand(command) return runCommand<Manifest>(effects, imageId, command, options)
const overlay = await Overlay.of(effects, imageId)
try {
for (let mount of options.mounts || []) {
await overlay.mount(mount.options, mount.path)
}
return await overlay.exec(commands)
} finally {
await overlay.destroy()
}
}, },
createDynamicAction: < createDynamicAction: <
@@ -654,3 +645,23 @@ export class StartSdk<Manifest extends SDKManifest, Store> {
} }
} }
} }
export async function runCommand<Manifest extends SDKManifest>(
effects: Effects,
imageId: Manifest["images"][number],
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)
try {
for (let mount of options.mounts || []) {
await overlay.mount(mount.options, mount.path)
}
return await overlay.exec(commands)
} finally {
await overlay.destroy()
}
}

View File

@@ -10,6 +10,7 @@ export * as config from "./config"
export * as configBuilder from "./config/builder" export * as configBuilder from "./config/builder"
export * as configTypes from "./config/configTypes" export * as configTypes from "./config/configTypes"
export * as dependencyConfig from "./dependencyConfig" export * as dependencyConfig from "./dependencyConfig"
export * as daemons from "./mainFn/Daemons"
export * as health from "./health" export * as health from "./health"
export * as healthFns from "./health/checkFns" export * as healthFns from "./health/checkFns"
export * as inits from "./inits" export * as inits from "./inits"
@@ -17,8 +18,10 @@ export * as mainFn from "./mainFn"
export * as manifest from "./manifest" export * as manifest from "./manifest"
export * as toml from "@iarna/toml" export * as toml from "@iarna/toml"
export * as types from "./types" export * as types from "./types"
export * as T from "./types"
export * as util from "./util" export * as util from "./util"
export * as yaml from "yaml" export * as yaml from "yaml"
export * as startSdk from "./StartSdk"
export * as matches from "ts-matches" export * as matches from "ts-matches"
export * as YAML from "yaml" export * as YAML from "yaml"

View File

@@ -43,7 +43,7 @@ type Daemon<
type ErrorDuplicateId<Id extends string> = `The id '${Id}' is already used` type ErrorDuplicateId<Id extends string> = `The id '${Id}' is already used`
const runDaemon = export const runDaemon =
<Manifest extends SDKManifest>() => <Manifest extends SDKManifest>() =>
async <A extends string>( async <A extends string>(
effects: Effects, effects: Effects,

View File

@@ -7,6 +7,8 @@ import { BindOptions, Scheme } from "./interfaces/Host"
import { Daemons } from "./mainFn/Daemons" import { Daemons } from "./mainFn/Daemons"
import { UrlString } from "./util/getServiceInterface" import { UrlString } from "./util/getServiceInterface"
export { SDKManifest } from "./manifest/ManifestTypes"
export type ExportedAction = (options: { export type ExportedAction = (options: {
effects: Effects effects: Effects
input?: Record<string, unknown> input?: Record<string, unknown>

View File

@@ -9,6 +9,8 @@ import "./Overlay"
import "./once" import "./once"
import { SDKManifest } from "../manifest/ManifestTypes" import { SDKManifest } from "../manifest/ManifestTypes"
export { GetServiceInterface, getServiceInterface } from "./getServiceInterface"
export { getServiceInterfaces } from "./getServiceInterfaces"
// prettier-ignore // prettier-ignore
export type FlattenIntersection<T> = export type FlattenIntersection<T> =
T extends ArrayLike<any> ? T : T extends ArrayLike<any> ? T :

View File

@@ -1,8 +1,8 @@
import { arrayOf, string } from "ts-matches" import { arrayOf, string } from "ts-matches"
import { ValidIfNoStupidEscape } from "../types" import { ValidIfNoStupidEscape } from "../types"
export const splitCommand = <A>( export const splitCommand = (
command: ValidIfNoStupidEscape<A> | [string, ...string[]], command: string | [string, ...string[]],
): string[] => { ): string[] => {
if (arrayOf(string).test(command)) return command if (arrayOf(string).test(command)) return command
return String(command) return String(command)