From 299d9998ad95fb0726e872fca3d374aacbb91e94 Mon Sep 17 00:00:00 2001 From: J H Date: Mon, 25 Mar 2024 12:01:13 -0600 Subject: [PATCH] chore: Making sure that the values that we are returning are valid now with the new types --- .../src/Adapters/HostSystemStartOs.ts | 5 - .../Systems/SystemForEmbassy/MainLoop.ts | 14 +- .../Systems/SystemForEmbassy/index.ts | 397 +++--------------- container-runtime/src/Models/JsonPath.ts | 1 + 4 files changed, 55 insertions(+), 362 deletions(-) diff --git a/container-runtime/src/Adapters/HostSystemStartOs.ts b/container-runtime/src/Adapters/HostSystemStartOs.ts index f173e4e62..5e52224fa 100644 --- a/container-runtime/src/Adapters/HostSystemStartOs.ts +++ b/container-runtime/src/Adapters/HostSystemStartOs.ts @@ -147,11 +147,6 @@ export class HostSystemStartOs implements Effects { T.Effects["exposeForDependents"] > } - exposeUi(...[options]: Parameters) { - return this.rpcRound("exposeUi", options) as ReturnType< - T.Effects["exposeUi"] - > - } getConfigured(...[]: Parameters) { return this.rpcRound("getConfigured", null) as ReturnType< T.Effects["getConfigured"] diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts index e5e532246..69e3e6615 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts @@ -25,15 +25,12 @@ export class MainLoop { wait: Promise }> | undefined - private propertiesEvent: NodeJS.Timeout | undefined constructor( readonly system: SystemForEmbassy, readonly effects: HostSystemStartOs, - readonly runProperties: () => Promise, ) { this.healthLoops = this.constructHealthLoops() this.mainEvent = this.constructMainEvent() - this.propertiesEvent = this.constructPropertiesEvent() } private async constructMainEvent() { @@ -85,23 +82,14 @@ export class MainLoop { } public async clean(options?: { timeout?: number }) { - const { mainEvent, healthLoops, propertiesEvent } = this + const { mainEvent, healthLoops } = this const main = await mainEvent delete this.mainEvent delete this.healthLoops - delete this.propertiesEvent if (mainEvent) await main?.daemon.term() - clearInterval(propertiesEvent) if (healthLoops) healthLoops.forEach((x) => clearInterval(x.interval)) } - private constructPropertiesEvent() { - const { runProperties } = this - return setInterval(() => { - runProperties() - }, EMBASSY_PROPERTIES_LOOP) - } - private constructHealthLoops() { const { manifest } = this.system const effects = this.effects diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index ae9b3f462..fb99795cc 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -59,6 +59,33 @@ export type PackagePropertyObject = { type: "object" description: string } + +const asProperty_ = ( + x: PackagePropertyString | PackagePropertyObject, +): T.PropertiesValue => { + if (x.type === "object") { + return { + ...x, + value: Object.fromEntries( + Object.entries(x.value).map(([key, value]) => [ + key, + asProperty_(value), + ]), + ), + } + } + return { + masked: false, + description: null, + qr: null, + copyable: null, + ...x, + } +} +const asProperty = (x: PackagePropertiesV2): T.PropertiesReturn => + Object.fromEntries( + Object.entries(x).map(([key, value]) => [key, asProperty_(value)]), + ) const [matchPackageProperties, setMatchPackageProperties] = deferred() const matchPackagePropertyObject: Parser = @@ -92,44 +119,6 @@ const matchProperties = object({ data: matchPackageProperties, }) -type ExportUi = { - values: { [key: string]: any } - expose: { [key: string]: T.ExposeUiPathsAll } -} - -function propertiesToExportUi( - properties: PackagePropertiesV2, - previousPath = "", -): ExportUi { - const exportUi: ExportUi = { - values: {}, - 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 - } - exportUi.values[key] = value.value - exportUi.expose[key] = { - type: "string", - path, - description: value.description ?? null, - masked: value.masked ?? false, - copyable: value.copyable ?? null, - qr: value.qr ?? null, - } - } - return exportUi -} - export class SystemForEmbassy implements System { currentRunning: MainLoop | undefined static async of(manifestLocation: string = MANIFEST_LOCATION) { @@ -236,6 +225,8 @@ export class SystemForEmbassy implements System { return this.getConfig(effects) case "/config/set": return this.setConfig(effects, input) + case "/properties": + return this.properties(effects) case "/actions/metadata": return todo() case "/init": @@ -279,9 +270,7 @@ export class SystemForEmbassy implements System { private async mainStart(effects: HostSystemStartOs): Promise { if (!!this.currentRunning) return - this.currentRunning = new MainLoop(this, effects, () => - this.properties(effects), - ) + this.currentRunning = new MainLoop(this, effects) } private async mainStop( effects: HostSystemStartOs, @@ -472,51 +461,44 @@ export class SystemForEmbassy implements System { } return { configured: true } } - private async properties(effects: HostSystemStartOs): Promise { + private async properties( + effects: HostSystemStartOs, + ): Promise> { // TODO BLU-J set the properties ever so often const setConfigValue = this.manifest.properties - if (!setConfigValue) return + if (!setConfigValue) throw new Error("There is no properties") if (setConfigValue.type === "docker") { const container = await DockerProcedureContainer.of( effects, setConfigValue, this.manifest.volumes, ) - const properties = JSON.parse( - ( - await container.exec([ - setConfigValue.entrypoint, - ...setConfigValue.args, - ]) - ).stdout.toString(), + const properties = matchProperties.unsafeCast( + JSON.parse( + ( + await container.exec([ + setConfigValue.entrypoint, + ...setConfigValue.args, + ]) + ).stdout.toString(), + ), ) - if (!matchProperties.test(properties)) return - const exposeUis = propertiesToExportUi(properties.data) - await effects.store.set({ - path: "/properties", - value: exposeUis.values, - }) - await effects.exposeUi(exposeUis.expose) + return asProperty(properties.data) } else if (setConfigValue.type === "script") { const moduleCode = this.moduleCode const method = moduleCode.properties if (!method) throw new Error("Expecting that the method properties exists") - const properties = await method( - new PolyfillEffects(effects, this.manifest), - ).then((x) => { - if ("result" in x) return x.result - if ("error" in x) throw new Error("Error getting config: " + x.error) - throw new Error("Error getting config: " + x["error-code"][1]) - }) - if (!matchProperties.test(properties)) return - const exposeUis = propertiesToExportUi(properties.data) - await effects.store.set({ - path: "/properties", - value: exposeUis.values, - }) - await effects.exposeUi(exposeUis.expose) + const properties = matchProperties.unsafeCast( + await method(new PolyfillEffects(effects, this.manifest)).then((x) => { + if ("result" in x) return x.result + if ("error" in x) throw new Error("Error getting config: " + x.error) + throw new Error("Error getting config: " + x["error-code"][1]) + }), + ) + return asProperty(properties.data) } + throw new Error(`Unknown type in the fetch properties: ${setConfigValue}`) } private async health( effects: HostSystemStartOs, @@ -651,279 +633,6 @@ export class SystemForEmbassy implements System { throw new Error("Error getting config: " + x["error-code"][1]) })) as any } - // private async sandbox( - // effects: HostSystemStartOs, - // options: { - // procedure: - // | "/createBackup" - // | "/restoreBackup" - // | "/getConfig" - // | "/setConfig" - // | "migration" - // | "/properties" - // | `/action/${string}` - // | `/dependencies/${string}/check` - // | `/dependencies/${string}/autoConfigure` - // input: unknown - // timeout?: number | undefined - // }, - // ): Promise { - // const input = options.input - // switch (options.procedure) { - // case "/createBackup": - // return this.roCreateBackup(effects) - // case "/restoreBackup": - // return this.roRestoreBackup(effects) - // case "/getConfig": - // return this.roGetConfig(effects) - // case "/setConfig": - // return this.roSetConfig(effects, input) - // case "migration": - // return this.roMigration(effects, input) - // case "/properties": - // return this.roProperties(effects) - // default: - // const procedure = options.procedure.split("/") - // switch (true) { - // case options.procedure.startsWith("/action/"): - // return this.roAction(effects, procedure[2], input) - // case options.procedure.startsWith("/dependencies/") && - // procedure[3] === "check": - // return this.roDependenciesCheck(effects, procedure[2], input) - - // case options.procedure.startsWith("/dependencies/") && - // procedure[3] === "autoConfigure": - // return this.roDependenciesAutoconfig(effects, procedure[2], input) - // } - // } - // } - - // private async roCreateBackup(effects: HostSystemStartOs): Promise { - // const backup = this.manifest.backup.create - // if (backup.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf(backup) - // await container.exec([backup.entrypoint, ...backup.args]) - // } else { - // const moduleCode = await this.moduleCode - // await moduleCode.createBackup?.(new PolyfillEffects(effects)) - // } - // } - // private async roRestoreBackup(effects: HostSystemStartOs): Promise { - // const restoreBackup = this.manifest.backup.restore - // if (restoreBackup.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf(restoreBackup) - // await container.exec([restoreBackup.entrypoint, ...restoreBackup.args]) - // } else { - // const moduleCode = await this.moduleCode - // await moduleCode.restoreBackup?.(new PolyfillEffects(effects)) - // } - // } - // private async roGetConfig(effects: HostSystemStartOs): Promise { - // const config = this.manifest.config?.get - // if (!config) return { spec: {} } - // if (config.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf(config) - // return JSON.parse( - // (await container.exec([config.entrypoint, ...config.args])).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.getConfig - // if (!method) throw new Error("Expecting that the method getConfig exists") - // return (await method(new PolyfillEffects(effects)).then((x) => { - // if ("result" in x) return x.result - // if ("error" in x) throw new Error("Error getting config: " + x.error) - // throw new Error("Error getting config: " + x["error-code"][1]) - // })) as any - // } - // } - // private async roSetConfig( - // effects: HostSystemStartOs, - // newConfig: unknown, - // ): Promise { - // const setConfigValue = this.manifest.config?.set - // if (!setConfigValue) return { signal: "SIGTERM", "depends-on": {} } - // if (setConfigValue.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf( - // setConfigValue, - // ) - // return JSON.parse( - // ( - // await container.exec([ - // setConfigValue.entrypoint, - // ...setConfigValue.args, - // JSON.stringify(newConfig), - // ]) - // ).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.setConfig - // if (!method) throw new Error("Expecting that the method setConfig exists") - // return await method( - // new PolyfillEffects(effects), - // newConfig as U.Config, - // ).then((x) => { - // if ("result" in x) return x.result - // if ("error" in x) throw new Error("Error getting config: " + x.error) - // throw new Error("Error getting config: " + x["error-code"][1]) - // }) - // } - // } - // private async roMigration( - // effects: HostSystemStartOs, - // fromVersion: unknown, - // ): Promise { - // throw new Error("Migrations should never be ran in the sandbox mode") - // } - // private async roProperties(effects: HostSystemStartOs): Promise { - // const setConfigValue = this.manifest.properties - // if (!setConfigValue) return {} - // if (setConfigValue.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf( - // setConfigValue, - // ) - // return JSON.parse( - // ( - // await container.exec([ - // setConfigValue.entrypoint, - // ...setConfigValue.args, - // ]) - // ).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.properties - // if (!method) - // throw new Error("Expecting that the method properties exists") - // return await method(new PolyfillEffects(effects)).then((x) => { - // if ("result" in x) return x.result - // if ("error" in x) throw new Error("Error getting config: " + x.error) - // throw new Error("Error getting config: " + x["error-code"][1]) - // }) - // } - // } - // private async roHealth( - // effects: HostSystemStartOs, - // healthId: string, - // timeSinceStarted: unknown, - // ): Promise { - // const healthProcedure = this.manifest["health-checks"][healthId] - // if (!healthProcedure) return - // if (healthProcedure.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf( - // healthProcedure, - // ) - // return JSON.parse( - // ( - // await container.exec([ - // healthProcedure.entrypoint, - // ...healthProcedure.args, - // JSON.stringify(timeSinceStarted), - // ]) - // ).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.health?.[healthId] - // if (!method) throw new Error("Expecting that the method health exists") - // await method(new PolyfillEffects(effects), Number(timeSinceStarted)).then( - // (x) => { - // if ("result" in x) return x.result - // if ("error" in x) throw new Error("Error getting config: " + x.error) - // throw new Error("Error getting config: " + x["error-code"][1]) - // }, - // ) - // } - // } - // private async roAction( - // effects: HostSystemStartOs, - // actionId: string, - // formData: unknown, - // ): Promise { - // const actionProcedure = this.manifest.actions?.[actionId]?.implementation - // if (!actionProcedure) return { message: "Action not found", value: null } - // if (actionProcedure.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf( - // actionProcedure, - // ) - // return JSON.parse( - // ( - // await container.exec([ - // actionProcedure.entrypoint, - // ...actionProcedure.args, - // JSON.stringify(formData), - // ]) - // ).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.action?.[actionId] - // if (!method) throw new Error("Expecting that the method action exists") - // return (await method(new PolyfillEffects(effects), formData as any).then( - // (x) => { - // if ("result" in x) return x.result - // if ("error" in x) throw new Error("Error getting config: " + x.error) - // throw new Error("Error getting config: " + x["error-code"][1]) - // }, - // )) as any - // } - // } - // private async roDependenciesCheck( - // effects: HostSystemStartOs, - // id: string, - // oldConfig: unknown, - // ): Promise { - // const actionProcedure = this.manifest.dependencies?.[id]?.config?.check - // if (!actionProcedure) return { message: "Action not found", value: null } - // if (actionProcedure.type === "docker") { - // const container = await DockerProcedureContainer.readonlyOf( - // actionProcedure, - // ) - // return JSON.parse( - // ( - // await container.exec([ - // actionProcedure.entrypoint, - // ...actionProcedure.args, - // JSON.stringify(oldConfig), - // ]) - // ).stdout, - // ) - // } else { - // const moduleCode = await this.moduleCode - // const method = moduleCode.dependencies?.[id]?.check - // if (!method) - // throw new Error( - // `Expecting that the method dependency check ${id} exists`, - // ) - // return (await method(new PolyfillEffects(effects), oldConfig as any).then( - // (x) => { - // if ("result" in x) return x.result - // if ("error" in x) throw new Error("Error getting config: " + x.error) - // throw new Error("Error getting config: " + x["error-code"][1]) - // }, - // )) as any - // } - // } - // private async roDependenciesAutoconfig( - // effects: HostSystemStartOs, - // id: string, - // oldConfig: unknown, - // ): Promise { - // const moduleCode = await this.moduleCode - // const method = moduleCode.dependencies?.[id]?.autoConfigure - // if (!method) - // throw new Error( - // `Expecting that the method dependency autoConfigure ${id} exists`, - // ) - // return (await method(new PolyfillEffects(effects), oldConfig as any).then( - // (x) => { - // if ("result" in x) return x.result - // if ("error" in x) throw new Error("Error getting config: " + x.error) - // throw new Error("Error getting config: " + x["error-code"][1]) - // }, - // )) as any - // } } async function removePointers(value: T.ConfigRes): Promise { const startingSpec = structuredClone(value.spec) diff --git a/container-runtime/src/Models/JsonPath.ts b/container-runtime/src/Models/JsonPath.ts index 627eb3be2..773331b19 100644 --- a/container-runtime/src/Models/JsonPath.ts +++ b/container-runtime/src/Models/JsonPath.ts @@ -35,6 +35,7 @@ export const jsonPath = some( "/backup/create", "/backup/restore", "/actions/metadata", + "/properties", ), string.refine(isNestedPath, "isNestedPath"), )