feat: Effects that shouldn't be needed removed from utils

This commit is contained in:
BluJ
2023-06-09 10:58:29 -06:00
parent 817913c9df
commit bc80cc974a
8 changed files with 108 additions and 49 deletions

View File

@@ -1,5 +1,6 @@
import { Effects } from "../../types" import { Effects } from "../../types"
import { createUtils } from "../../util" import { createUtils } from "../../util"
import { stringFromStdErrOut } from "../../util/stringFromStdErrOut"
import { CheckResult } from "./CheckResult" import { CheckResult } from "./CheckResult"
export function containsAddress(x: string, port: number) { export function containsAddress(x: string, port: number) {
const readPorts = x const readPorts = x
@@ -32,10 +33,17 @@ export async function checkPortListening(
Promise.resolve().then(async () => { Promise.resolve().then(async () => {
const hasAddress = const hasAddress =
containsAddress( containsAddress(
await utils.runCommand(`cat /proc/net/tcp`, {}), await utils.childProcess
.exec(`cat /proc/net/tcp`, {})
.then(stringFromStdErrOut),
port, port,
) || ) ||
containsAddress(await utils.runCommand("cat /proc/net/udp", {}), port) containsAddress(
await utils.childProcess
.exec("cat /proc/net/udp", {})
.then(stringFromStdErrOut),
port,
)
if (hasAddress) { if (hasAddress) {
return { status: "passing", message: options.successMessage } return { status: "passing", message: options.successMessage }
} }

View File

@@ -1,5 +1,6 @@
import { CommandType, Effects } from "../../types" import { CommandType, Effects } from "../../types"
import { createUtils } from "../../util" import { createUtils } from "../../util"
import { stringFromStdErrOut } from "../../util/stringFromStdErrOut"
import { CheckResult } from "./CheckResult" import { CheckResult } from "./CheckResult"
import { timeoutPromise } from "./index" import { timeoutPromise } from "./index"
@@ -22,7 +23,7 @@ export const runHealthScript = async (
): Promise<CheckResult> => { ): Promise<CheckResult> => {
const utils = createUtils(effects) const utils = createUtils(effects)
const res = await Promise.race([ const res = await Promise.race([
utils.runCommand(runCommand, { timeout }), utils.childProcess.exec(runCommand, { timeout }).then(stringFromStdErrOut),
timeoutPromise(timeout), timeoutPromise(timeout),
]).catch((e) => { ]).catch((e) => {
console.warn(errorMessage) console.warn(errorMessage)

View File

@@ -7,7 +7,7 @@ import { DaemonReturned, Effects, ValidIfNoStupidEscape } from "../types"
import { createUtils } from "../util" import { createUtils } from "../util"
type Daemon<Ids extends string, Command extends string, Id extends string> = { type Daemon<Ids extends string, Command extends string, Id extends string> = {
id: "" extends Id ? never : Id id: "" extends Id ? never : Id
command: string command: ValidIfNoStupidEscape<Command> | [string, ...string[]]
env?: Record<string, string> env?: Record<string, string>
ready: { ready: {
display: string | null display: string | null

View File

@@ -0,0 +1,38 @@
import { getHostname } from "../util/getNetworkInterface"
import { splitCommand } from "../util/splitCommand"
describe("splitCommand ", () => {
const inputToExpected = [
["cat", ["cat"]],
[["cat"], ["cat"]],
[
["cat", "hello all my homies"],
["cat", "hello all my homies"],
],
["cat hello world", ["cat", "hello", "world"]],
["cat hello 'big world'", ["cat", "hello", "big world"]],
[`cat hello "big world"`, ["cat", "hello", "big world"]],
// Too many spaces
["cat ", ["cat"]],
[["cat "], ["cat "]],
[
["cat ", "hello all my homies "],
["cat ", "hello all my homies "],
],
["cat hello world ", ["cat", "hello", "world"]],
[
" cat hello 'big world' ",
["cat", "hello", "big world"],
],
[
` cat hello "big world" `,
["cat", "hello", "big world"],
],
]
for (const [input, expectValue] of inputToExpected) {
test(`should return ${expectValue} for ${input}`, () => {
expect(splitCommand(input as any)).toEqual(expectValue)
})
}
})

View File

@@ -10,7 +10,6 @@ export const getHostname = (url: string): HostName | null => {
if (!founds) return null if (!founds) return null
const parts = founds.split("@") const parts = founds.split("@")
const last = parts[parts.length - 1] as HostName | null const last = parts[parts.length - 1] as HostName | null
console.log({ url, parts, founds, last })
return last return last
} }

17
lib/util/splitCommand.ts Normal file
View File

@@ -0,0 +1,17 @@
import { arrayOf, string } from "ts-matches"
import { ValidIfNoStupidEscape } from "../types"
export const splitCommand = <A>(
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
): string[] => {
if (arrayOf(string).test(command)) return command
return String(command)
.split('"')
.flatMap((x, i) =>
i % 2 !== 0
? [x]
: x.split("'").flatMap((x, i) => (i % 2 !== 0 ? [x] : x.split(" "))),
)
.map((x) => x.trim())
.filter(Boolean)
}

View File

@@ -0,0 +1,6 @@
export async function stringFromStdErrOut(x: {
stdout: string
stderr: string
}) {
return x?.stderr ? Promise.reject(x.stderr) : x.stdout
}

View File

@@ -1,4 +1,3 @@
import FileHelper from "./fileHelper"
import nullIfEmpty from "./nullIfEmpty" import nullIfEmpty from "./nullIfEmpty"
import { import {
CheckResult, CheckResult,
@@ -12,6 +11,7 @@ import {
ExtractStore, ExtractStore,
InterfaceId, InterfaceId,
PackageId, PackageId,
ValidIfNoStupidEscape,
} from "../types" } from "../types"
import { GetSystemSmtp } from "./GetSystemSmtp" import { GetSystemSmtp } from "./GetSystemSmtp"
import { DefaultString } from "../config/configTypes" import { DefaultString } from "../config/configTypes"
@@ -34,8 +34,14 @@ import {
GetNetworkInterfaces, GetNetworkInterfaces,
getNetworkInterfaces, getNetworkInterfaces,
} from "./getNetworkInterfaces" } from "./getNetworkInterfaces"
import * as CP from "node:child_process"
import { promisify } from "node:util"
import { splitCommand } from "./splitCommand"
import { exec } from "node:child_process" const childProcess = {
exec: promisify(CP.exec),
execFile: promisify(CP.execFile),
}
export type Utils<Store, WrapperOverWrite = { const: never }> = { export type Utils<Store, WrapperOverWrite = { const: never }> = {
checkPortListening( checkPortListening(
@@ -55,6 +61,7 @@ export type Utils<Store, WrapperOverWrite = { const: never }> = {
errorMessage?: string errorMessage?: string
}, },
): Promise<CheckResult> ): Promise<CheckResult>
childProcess: typeof childProcess
createInterface: (options: { createInterface: (options: {
name: string name: string
id: string id: string
@@ -93,15 +100,10 @@ export type Utils<Store, WrapperOverWrite = { const: never }> = {
}) => GetNetworkInterfaces & WrapperOverWrite }) => GetNetworkInterfaces & WrapperOverWrite
} }
nullIfEmpty: typeof nullIfEmpty nullIfEmpty: typeof nullIfEmpty
readFile: <A>(fileHelper: FileHelper<A>) => ReturnType<FileHelper<A>["read"]> runDaemon: <A extends string>(
runDaemon: ( command: ValidIfNoStupidEscape<A> | [string, ...string[]],
command: string,
options: { env?: Record<string, string> }, options: { env?: Record<string, string> },
) => Promise<DaemonReturned> ) => Promise<DaemonReturned>
runCommand: (
command: string,
options: { env?: Record<string, string>; timeout?: number },
) => Promise<string>
store: { store: {
get: <Path extends string>( get: <Path extends string>(
packageId: string, packageId: string,
@@ -115,10 +117,6 @@ export type Utils<Store, WrapperOverWrite = { const: never }> = {
value: ExtractStore<Store, Path>, value: ExtractStore<Store, Path>,
) => Promise<void> ) => Promise<void>
} }
writeFile: <A>(
fileHelper: FileHelper<A>,
data: A,
) => ReturnType<FileHelper<A>["write"]>
} }
export const utils = <Store = never, WrapperOverWrite = { const: never }>( export const utils = <Store = never, WrapperOverWrite = { const: never }>(
effects: Effects, effects: Effects,
@@ -135,6 +133,7 @@ export const utils = <Store = never, WrapperOverWrite = { const: never }>(
path: string path: string
search: Record<string, string> search: Record<string, string>
}) => new NetworkInterfaceBuilder({ ...options, effects }), }) => new NetworkInterfaceBuilder({ ...options, effects }),
childProcess,
getSystemSmtp: () => getSystemSmtp: () =>
new GetSystemSmtp(effects) as GetSystemSmtp & WrapperOverWrite, new GetSystemSmtp(effects) as GetSystemSmtp & WrapperOverWrite,
@@ -143,9 +142,6 @@ export const utils = <Store = never, WrapperOverWrite = { const: never }>(
single: (id: string) => new SingleHost({ id, effects }), single: (id: string) => new SingleHost({ id, effects }),
multi: (id: string) => new MultiHost({ id, effects }), multi: (id: string) => new MultiHost({ id, effects }),
}, },
readFile: <A>(fileHelper: FileHelper<A>) => fileHelper.read(effects),
writeFile: <A>(fileHelper: FileHelper<A>, data: A) =>
fileHelper.write(data, effects),
nullIfEmpty, nullIfEmpty,
networkInterface: { networkInterface: {
@@ -178,45 +174,39 @@ export const utils = <Store = never, WrapperOverWrite = { const: never }>(
) => effects.store.set<Store, Path>({ value, path: path as any }), ) => effects.store.set<Store, Path>({ value, path: path as any }),
}, },
runDaemon: async ( runDaemon: async <A extends string>(
command: string, command: ValidIfNoStupidEscape<A> | [string, ...string[]],
options: { env?: Record<string, string> }, options: { env?: Record<string, string> },
): Promise<DaemonReturned> => { ): Promise<DaemonReturned> => {
let childProcess: null | { const commands = splitCommand(command)
kill(signal?: number | string): void const childProcess = CP.spawn(commands[0], commands.slice(1), options)
} = null const answer = new Promise<string>((resolve, reject) => {
const answer = new Promise<string>( const output: string[] = []
(resolve, reject) => childProcess.stdout.on("data", (data) => {
(childProcess = exec(command, options, (error, stdout, stderr) => { output.push(data.toString())
if (error) return reject(error.toString()) })
if (stderr) return reject(stderr.toString()) const outputError: string[] = []
return resolve(stdout.toString()) childProcess.stderr.on("data", (data) => {
})), outputError.push(data.toString())
) })
childProcess.on("close", (code) => {
if (code === 0) {
return resolve(output.join(""))
}
return reject(outputError.join(""))
})
})
return { return {
wait() { wait() {
return answer return answer
}, },
async term() { async term() {
childProcess?.kill() childProcess.kill()
}, },
} }
}, },
runCommand: async (
command: string,
options: { env?: Record<string, string>; timeout?: number },
): Promise<string> => {
const answer = new Promise<string>((resolve, reject) =>
exec(command, options, (error, stdout, stderr) => {
if (error) return reject(error.toString())
if (stderr) return reject(stderr.toString())
return resolve(stdout.toString())
}),
)
return answer
},
checkPortListening: checkPortListening.bind(null, effects), checkPortListening: checkPortListening.bind(null, effects),
checkWebUrl: checkWebUrl.bind(null, effects), checkWebUrl: checkWebUrl.bind(null, effects),