mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
Merge branch 'next/minor' of github.com:Start9Labs/start-os into next/major
This commit is contained in:
@@ -4,6 +4,8 @@ Description=StartOS Container Runtime
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/node --experimental-detect-module --unhandled-rejections=warn /usr/lib/startos/init/index.js
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
4008
container-runtime/package-lock.json
generated
4008
container-runtime/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -198,6 +198,9 @@ export function makeEffects(context: EffectContext): Effects {
|
||||
T.Effects["getContainerIp"]
|
||||
>
|
||||
},
|
||||
getOsIp(...[]: Parameters<T.Effects["getOsIp"]>) {
|
||||
return rpcRound("get-os-ip", {}) as ReturnType<T.Effects["getOsIp"]>
|
||||
},
|
||||
getHostInfo: ((...[allOptions]: Parameters<T.Effects["getHostInfo"]>) => {
|
||||
const options = {
|
||||
...allOptions,
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { ExtendedVersion, types as T, utils } from "@start9labs/start-sdk"
|
||||
import {
|
||||
ExtendedVersion,
|
||||
types as T,
|
||||
utils,
|
||||
VersionRange,
|
||||
} from "@start9labs/start-sdk"
|
||||
import * as fs from "fs/promises"
|
||||
|
||||
import { polyfillEffects } from "./polyfillEffects"
|
||||
@@ -345,13 +350,11 @@ export class SystemForEmbassy implements System {
|
||||
const previousVersion = await effects.getDataVersion()
|
||||
if (previousVersion) {
|
||||
if (
|
||||
(await this.migration(effects, previousVersion, timeoutMs)).configured
|
||||
(await this.migration(effects, { from: previousVersion }, timeoutMs))
|
||||
.configured
|
||||
) {
|
||||
await effects.action.clearRequests({ only: ["needs-config"] })
|
||||
}
|
||||
await effects.setDataVersion({
|
||||
version: ExtendedVersion.parseEmver(this.manifest.version).toString(),
|
||||
})
|
||||
} else if (this.manifest.config) {
|
||||
await effects.action.request({
|
||||
packageId: this.manifest.id,
|
||||
@@ -361,6 +364,9 @@ export class SystemForEmbassy implements System {
|
||||
reason: "This service must be configured before it can be run",
|
||||
})
|
||||
}
|
||||
await effects.setDataVersion({
|
||||
version: ExtendedVersion.parseEmver(this.manifest.version).toString(),
|
||||
})
|
||||
}
|
||||
async exportNetwork(effects: Effects) {
|
||||
for (const [id, interfaceValue] of Object.entries(
|
||||
@@ -542,7 +548,10 @@ export class SystemForEmbassy implements System {
|
||||
nextVersion: Optional<string>,
|
||||
timeoutMs: number | null,
|
||||
): Promise<void> {
|
||||
// TODO Do a migration down if the version exists
|
||||
await this.currentRunning?.clean({ timeout: timeoutMs ?? undefined })
|
||||
if (nextVersion) {
|
||||
await this.migration(effects, { to: nextVersion }, timeoutMs)
|
||||
}
|
||||
await effects.setMainStatus({ status: "stopped" })
|
||||
}
|
||||
|
||||
@@ -746,46 +755,37 @@ export class SystemForEmbassy implements System {
|
||||
|
||||
async migration(
|
||||
effects: Effects,
|
||||
fromVersion: string,
|
||||
version: { from: string } | { to: string },
|
||||
timeoutMs: number | null,
|
||||
): Promise<{ configured: boolean }> {
|
||||
const fromEmver = ExtendedVersion.parseEmver(fromVersion)
|
||||
const currentEmver = ExtendedVersion.parseEmver(this.manifest.version)
|
||||
if (!this.manifest.migrations) return { configured: true }
|
||||
const fromMigration = Object.entries(this.manifest.migrations.from)
|
||||
.map(
|
||||
([version, procedure]) =>
|
||||
[ExtendedVersion.parseEmver(version), procedure] as const,
|
||||
)
|
||||
.find(
|
||||
([versionEmver, procedure]) =>
|
||||
versionEmver.greaterThan(fromEmver) &&
|
||||
versionEmver.lessThanOrEqual(currentEmver),
|
||||
)
|
||||
const toMigration = Object.entries(this.manifest.migrations.to)
|
||||
.map(
|
||||
([version, procedure]) =>
|
||||
[ExtendedVersion.parseEmver(version), procedure] as const,
|
||||
)
|
||||
.find(
|
||||
([versionEmver, procedure]) =>
|
||||
versionEmver.greaterThan(fromEmver) &&
|
||||
versionEmver.lessThanOrEqual(currentEmver),
|
||||
)
|
||||
|
||||
// prettier-ignore
|
||||
const migration = (
|
||||
fromEmver.greaterThan(currentEmver) ? [toMigration, fromMigration] :
|
||||
[fromMigration, toMigration]).filter(Boolean)[0]
|
||||
let migration
|
||||
let args: [string, ...string[]]
|
||||
if ("from" in version) {
|
||||
args = [version.from, "from"]
|
||||
const fromExver = ExtendedVersion.parse(version.from)
|
||||
if (!this.manifest.migrations) return { configured: true }
|
||||
migration = Object.entries(this.manifest.migrations.from)
|
||||
.map(
|
||||
([version, procedure]) =>
|
||||
[VersionRange.parseEmver(version), procedure] as const,
|
||||
)
|
||||
.find(([versionEmver, _]) => versionEmver.satisfiedBy(fromExver))
|
||||
} else {
|
||||
args = [version.to, "to"]
|
||||
const toExver = ExtendedVersion.parse(version.to)
|
||||
if (!this.manifest.migrations) return { configured: true }
|
||||
migration = Object.entries(this.manifest.migrations.to)
|
||||
.map(
|
||||
([version, procedure]) =>
|
||||
[VersionRange.parseEmver(version), procedure] as const,
|
||||
)
|
||||
.find(([versionEmver, _]) => versionEmver.satisfiedBy(toExver))
|
||||
}
|
||||
|
||||
if (migration) {
|
||||
const [version, procedure] = migration
|
||||
const [_, procedure] = migration
|
||||
if (procedure.type === "docker") {
|
||||
const commands = [
|
||||
procedure.entrypoint,
|
||||
...procedure.args,
|
||||
JSON.stringify(fromVersion),
|
||||
]
|
||||
const commands = [procedure.entrypoint, ...procedure.args]
|
||||
const container = await DockerProcedureContainer.of(
|
||||
effects,
|
||||
this.manifest.id,
|
||||
@@ -794,7 +794,11 @@ export class SystemForEmbassy implements System {
|
||||
`Migration - ${commands.join(" ")}`,
|
||||
)
|
||||
return JSON.parse(
|
||||
(await container.execFail(commands, timeoutMs)).stdout.toString(),
|
||||
(
|
||||
await container.execFail(commands, timeoutMs, {
|
||||
input: JSON.stringify(args[0]),
|
||||
})
|
||||
).stdout.toString(),
|
||||
)
|
||||
} else if (procedure.type === "script") {
|
||||
const moduleCode = await this.moduleCode
|
||||
@@ -803,7 +807,7 @@ export class SystemForEmbassy implements System {
|
||||
throw new Error("Expecting that the method migration exists")
|
||||
return (await method(
|
||||
polyfillEffects(effects, this.manifest),
|
||||
fromVersion as string,
|
||||
...args,
|
||||
).then((x) => {
|
||||
if ("result" in x) return x.result
|
||||
if ("error" in x) throw new Error("Error getting config: " + x.error)
|
||||
@@ -974,7 +978,7 @@ export class SystemForEmbassy implements System {
|
||||
})) as U.Config
|
||||
if (!oldConfig) return
|
||||
const moduleCode = await this.moduleCode
|
||||
const method = moduleCode.dependencies?.[id]?.autoConfigure
|
||||
const method = moduleCode?.dependencies?.[id]?.autoConfigure
|
||||
if (!method) return
|
||||
const newConfig = (await method(
|
||||
polyfillEffects(effects, this.manifest),
|
||||
@@ -1177,9 +1181,14 @@ function extractServiceInterfaceId(manifest: Manifest, specInterface: string) {
|
||||
return serviceInterfaceId
|
||||
}
|
||||
async function convertToNewConfig(value: OldGetConfigRes) {
|
||||
const valueSpec: OldConfigSpec = matchOldConfigSpec.unsafeCast(value.spec)
|
||||
const spec = transformConfigSpec(valueSpec)
|
||||
if (!value.config) return { spec, config: null }
|
||||
const config = transformOldConfigToNew(valueSpec, value.config)
|
||||
return { spec, config }
|
||||
try {
|
||||
const valueSpec: OldConfigSpec = matchOldConfigSpec.unsafeCast(value.spec)
|
||||
const spec = transformConfigSpec(valueSpec)
|
||||
if (!value.config) return { spec, config: null }
|
||||
const config = transformOldConfigToNew(valueSpec, value.config) ?? null
|
||||
return { spec, config }
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,8 +444,8 @@ function parseDfOutput(output: string): { used: number; total: number } {
|
||||
.map((x) => x.split(/\s+/))
|
||||
const index = lines.splice(0, 1)[0].map((x) => x.toLowerCase())
|
||||
const usedIndex = index.indexOf("used")
|
||||
const availableIndex = index.indexOf("available")
|
||||
const sizeIndex = index.indexOf("size")
|
||||
const used = lines.map((x) => Number.parseInt(x[usedIndex]))[0] || 0
|
||||
const total = lines.map((x) => Number.parseInt(x[availableIndex]))[0] || 0
|
||||
const total = lines.map((x) => Number.parseInt(x[sizeIndex]))[0] || 0
|
||||
return { used, total }
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ export function transformOldConfigToNew(
|
||||
spec: OldConfigSpec,
|
||||
config: Record<string, any>,
|
||||
): Record<string, any> {
|
||||
if (!config) return config
|
||||
return Object.entries(spec).reduce((obj, [key, val]) => {
|
||||
let newVal = config[key]
|
||||
|
||||
@@ -157,9 +158,16 @@ export function transformOldConfigToNew(
|
||||
}
|
||||
|
||||
if (isUnion(val)) {
|
||||
const selection = config[key][val.tag.id]
|
||||
if (!config[key]) return obj
|
||||
|
||||
const selection = config[key]?.[val.tag.id]
|
||||
|
||||
if (!selection) return obj
|
||||
|
||||
delete config[key][val.tag.id]
|
||||
|
||||
if (!val.variants[selection]) return obj
|
||||
|
||||
newVal = {
|
||||
selection,
|
||||
value: transformOldConfigToNew(
|
||||
@@ -170,6 +178,8 @@ export function transformOldConfigToNew(
|
||||
}
|
||||
|
||||
if (isList(val) && isObjectList(val)) {
|
||||
if (!config[key]) return obj
|
||||
|
||||
newVal = (config[key] as object[]).map((obj) =>
|
||||
transformOldConfigToNew(
|
||||
matchOldConfigSpec.unsafeCast(val.spec.spec),
|
||||
|
||||
Reference in New Issue
Block a user