mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
Feature/disk usage (#2637)
* feat: Add disk usage * Fixed: let the set config work with nesting. * chore: Changes * chore: Add default route * fix: Tor only config * chore
This commit is contained in:
@@ -2,7 +2,7 @@ import { types as T, utils, EmVer } from "@start9labs/start-sdk"
|
||||
import * as fs from "fs/promises"
|
||||
|
||||
import { PolyfillEffects } from "./polyfillEffects"
|
||||
import { Duration, duration } from "../../../Models/Duration"
|
||||
import { Duration, duration, fromDuration } from "../../../Models/Duration"
|
||||
import { System } from "../../../Interfaces/System"
|
||||
import { matchManifest, Manifest, Procedure } from "./matchManifest"
|
||||
import * as childProcess from "node:child_process"
|
||||
@@ -478,10 +478,13 @@ export class SystemForEmbassy implements System {
|
||||
delete this.currentRunning
|
||||
if (currentRunning) {
|
||||
await currentRunning.clean({
|
||||
timeout: this.manifest.main["sigterm-timeout"],
|
||||
timeout: fromDuration(this.manifest.main["sigterm-timeout"]),
|
||||
})
|
||||
}
|
||||
const durationValue = duration(this.manifest.main["sigterm-timeout"], "s")
|
||||
const durationValue = duration(
|
||||
fromDuration(this.manifest.main["sigterm-timeout"]),
|
||||
"s",
|
||||
)
|
||||
return durationValue
|
||||
}
|
||||
private async createBackup(
|
||||
@@ -967,7 +970,7 @@ async function updateConfig(
|
||||
|
||||
const newConfigValue = mutConfigValue[key]
|
||||
if (matchSpec.test(specValue)) {
|
||||
const updateObject = { spec: null }
|
||||
const updateObject = { spec: newConfigValue }
|
||||
await updateConfig(
|
||||
effects,
|
||||
manifest,
|
||||
@@ -1001,6 +1004,10 @@ async function updateConfig(
|
||||
manifest,
|
||||
specInterface,
|
||||
)
|
||||
if (!serviceInterfaceId) {
|
||||
mutConfigValue[key] = ""
|
||||
return
|
||||
}
|
||||
const filled = await utils
|
||||
.getServiceInterface(effects, {
|
||||
packageId: specValue["package-id"],
|
||||
@@ -1035,8 +1042,16 @@ async function updateConfig(
|
||||
}
|
||||
}
|
||||
function extractServiceInterfaceId(manifest: Manifest, specInterface: string) {
|
||||
let serviceInterfaceId
|
||||
const lanConfig = manifest.interfaces[specInterface]?.["lan-config"] || {}
|
||||
serviceInterfaceId = `${specInterface}-${Object.entries(lanConfig)[0]?.[1]?.internal}`
|
||||
const internalPort =
|
||||
Object.entries(
|
||||
manifest.interfaces[specInterface]?.["lan-config"] || {},
|
||||
)[0]?.[1]?.internal ||
|
||||
Object.entries(
|
||||
manifest.interfaces[specInterface]?.["tor-config"]?.["port-mapping"] ||
|
||||
{},
|
||||
)?.[0]?.[1]
|
||||
|
||||
if (!internalPort) return null
|
||||
const serviceInterfaceId = `${specInterface}-${internalPort}`
|
||||
return serviceInterfaceId
|
||||
}
|
||||
|
||||
@@ -120,6 +120,10 @@ export type Effects = {
|
||||
/// Returns the body as a json
|
||||
json(): Promise<unknown>
|
||||
}>
|
||||
diskUsage(options?: {
|
||||
volumeId: string
|
||||
path: string
|
||||
}): Promise<{ used: number; total: number }>
|
||||
|
||||
runRsync(options: {
|
||||
srcVolume: string
|
||||
|
||||
@@ -8,7 +8,8 @@ import { HostSystemStartOs } from "../../HostSystemStartOs"
|
||||
import "isomorphic-fetch"
|
||||
import { Manifest } from "./matchManifest"
|
||||
import { DockerProcedureContainer } from "./DockerProcedureContainer"
|
||||
|
||||
import * as cp from "child_process"
|
||||
export const execFile = promisify(cp.execFile)
|
||||
export class PolyfillEffects implements oet.Effects {
|
||||
constructor(
|
||||
readonly effects: HostSystemStartOs,
|
||||
@@ -104,7 +105,9 @@ export class PolyfillEffects implements oet.Effects {
|
||||
stderr: x.stderr.toString(),
|
||||
stdout: x.stdout.toString(),
|
||||
}))
|
||||
.then((x) => (!!x.stderr ? { error: x.stderr } : { result: x.stdout }))
|
||||
.then((x: any) =>
|
||||
!!x.stderr ? { error: x.stderr } : { result: x.stdout },
|
||||
)
|
||||
}
|
||||
runDaemon(input: { command: string; args?: string[] | undefined }): {
|
||||
wait(): Promise<oet.ResultType<string>>
|
||||
@@ -163,7 +166,7 @@ export class PolyfillEffects implements oet.Effects {
|
||||
stderr: x.stderr.toString(),
|
||||
stdout: x.stdout.toString(),
|
||||
}))
|
||||
.then((x) => {
|
||||
.then((x: any) => {
|
||||
if (!!x.stderr) {
|
||||
throw new Error(x.stderr)
|
||||
}
|
||||
@@ -198,7 +201,7 @@ export class PolyfillEffects implements oet.Effects {
|
||||
stderr: x.stderr.toString(),
|
||||
stdout: x.stdout.toString(),
|
||||
}))
|
||||
.then((x) => {
|
||||
.then((x: any) => {
|
||||
if (!!x.stderr) {
|
||||
throw new Error(x.stderr)
|
||||
}
|
||||
@@ -352,7 +355,7 @@ export class PolyfillEffects implements oet.Effects {
|
||||
return String(pid)
|
||||
}
|
||||
const waitPromise = new Promise<null>((resolve, reject) => {
|
||||
spawned.on("exit", (code) => {
|
||||
spawned.on("exit", (code: any) => {
|
||||
if (code === 0) {
|
||||
resolve(null)
|
||||
} else {
|
||||
@@ -364,4 +367,55 @@ export class PolyfillEffects implements oet.Effects {
|
||||
const progress = () => Promise.resolve(percentage)
|
||||
return { id, wait, progress }
|
||||
}
|
||||
async diskUsage(
|
||||
options?: { volumeId: string; path: string } | undefined,
|
||||
): Promise<{ used: number; total: number }> {
|
||||
const output = await execFile("df", ["--block-size=1", "-P", "/"])
|
||||
.then((x: any) => ({
|
||||
stderr: x.stderr.toString(),
|
||||
stdout: x.stdout.toString(),
|
||||
}))
|
||||
.then((x: any) => {
|
||||
if (!!x.stderr) {
|
||||
throw new Error(x.stderr)
|
||||
}
|
||||
return parseDfOutput(x.stdout)
|
||||
})
|
||||
if (!!options) {
|
||||
const used = await execFile("du", [
|
||||
"-s",
|
||||
"--block-size=1",
|
||||
"-P",
|
||||
new Volume(options.volumeId, options.path).path,
|
||||
])
|
||||
.then((x: any) => ({
|
||||
stderr: x.stderr.toString(),
|
||||
stdout: x.stdout.toString(),
|
||||
}))
|
||||
.then((x: any) => {
|
||||
if (!!x.stderr) {
|
||||
throw new Error(x.stderr)
|
||||
}
|
||||
return Number.parseInt(x.stdout.split(/\s+/)[0])
|
||||
})
|
||||
return {
|
||||
...output,
|
||||
used,
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
function parseDfOutput(output: string): { used: number; total: number } {
|
||||
const lines = output
|
||||
.split("\n")
|
||||
.filter((x) => x.length)
|
||||
.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 used = lines.map((x) => Number.parseInt(x[usedIndex]))[0] || 0
|
||||
const total = lines.map((x) => Number.parseInt(x[availableIndex]))[0] || 0
|
||||
return { used, total }
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import {
|
||||
literals,
|
||||
number,
|
||||
Parser,
|
||||
some,
|
||||
} from "ts-matches"
|
||||
import { matchDuration } from "./Duration"
|
||||
|
||||
const VolumeId = string
|
||||
const Path = string
|
||||
@@ -31,7 +33,7 @@ export const matchDockerProcedure = object(
|
||||
"toml",
|
||||
"toml-pretty",
|
||||
),
|
||||
"sigterm-timeout": number,
|
||||
"sigterm-timeout": some(number, matchDuration),
|
||||
inject: boolean,
|
||||
},
|
||||
["io-format", "sigterm-timeout", "system", "args", "inject", "mounts"],
|
||||
|
||||
@@ -1,6 +1,30 @@
|
||||
export type TimeUnit = "d" | "h" | "s" | "ms"
|
||||
import { string } from "ts-matches"
|
||||
|
||||
export type TimeUnit = "d" | "h" | "s" | "ms" | "m" | "µs" | "ns"
|
||||
export type Duration = `${number}${TimeUnit}`
|
||||
|
||||
const durationRegex = /^([0-9]*(\.[0-9]+)?)(ns|µs|ms|s|m|d)$/
|
||||
|
||||
export const matchDuration = string.refine(isDuration)
|
||||
export function isDuration(value: string): value is Duration {
|
||||
return durationRegex.test(value)
|
||||
}
|
||||
|
||||
export function duration(timeValue: number, timeUnit: TimeUnit = "s") {
|
||||
return `${timeValue > 0 ? timeValue : 0}${timeUnit}` as Duration
|
||||
}
|
||||
const unitsToSeconds: Record<string, number> = {
|
||||
ns: 1e-9,
|
||||
µs: 1e-6,
|
||||
ms: 0.001,
|
||||
s: 1,
|
||||
m: 60,
|
||||
h: 3600,
|
||||
d: 86400,
|
||||
}
|
||||
|
||||
export function fromDuration(duration: Duration | number): number {
|
||||
if (typeof duration === "number") return duration
|
||||
const [, num, , unit] = duration.match(durationRegex) || []
|
||||
return Number(num) * unitsToSeconds[unit]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user