mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
feat: Get the health checks for the js
This commit is contained in:
@@ -268,6 +268,7 @@ export class HostSystemStartOs implements Effects {
|
||||
>
|
||||
}
|
||||
setHealth(...[options]: Parameters<T.Effects["setHealth"]>) {
|
||||
console.error("BLUJ sethealth", options)
|
||||
return this.rpcRound("setHealth", options) as ReturnType<
|
||||
T.Effects["setHealth"]
|
||||
>
|
||||
|
||||
@@ -106,46 +106,106 @@ export class MainLoop {
|
||||
const { manifest } = this.system
|
||||
const effects = this.effects
|
||||
const start = Date.now()
|
||||
return Object.values(manifest["health-checks"]).map((value) => {
|
||||
const name = value.name
|
||||
const interval = setInterval(async () => {
|
||||
const actionProcedure = value
|
||||
const timeChanged = Date.now() - start
|
||||
if (actionProcedure.type === "docker") {
|
||||
const container = await DockerProcedureContainer.of(
|
||||
effects,
|
||||
actionProcedure,
|
||||
manifest.volumes,
|
||||
)
|
||||
const executed = await container.exec([
|
||||
actionProcedure.entrypoint,
|
||||
...actionProcedure.args,
|
||||
JSON.stringify(timeChanged),
|
||||
])
|
||||
const stderr = executed.stderr.toString()
|
||||
if (stderr)
|
||||
console.error(`Error running health check ${value.name}: ${stderr}`)
|
||||
return executed.stdout.toString()
|
||||
} else {
|
||||
const moduleCode = await this.system.moduleCode
|
||||
const method = moduleCode.health?.[value.name]
|
||||
if (!method)
|
||||
return console.error(
|
||||
`Expecting that thejs health check ${value.name} exists`,
|
||||
return Object.entries(manifest["health-checks"]).map(
|
||||
([healthId, value]) => {
|
||||
const interval = setInterval(async () => {
|
||||
const actionProcedure = value
|
||||
const timeChanged = Date.now() - start
|
||||
if (actionProcedure.type === "docker") {
|
||||
const container = await DockerProcedureContainer.of(
|
||||
effects,
|
||||
actionProcedure,
|
||||
manifest.volumes,
|
||||
)
|
||||
return (await method(
|
||||
new PolyfillEffects(effects, this.system.manifest),
|
||||
timeChanged,
|
||||
).then((x) => {
|
||||
if ("result" in x) return x.result
|
||||
if ("error" in x)
|
||||
return console.error("Error getting config: " + x.error)
|
||||
return console.error("Error getting config: " + x["error-code"][1])
|
||||
})) as any
|
||||
}
|
||||
}, EMBASSY_HEALTH_INTERVAL)
|
||||
const executed = await container.exec([
|
||||
actionProcedure.entrypoint,
|
||||
...actionProcedure.args,
|
||||
JSON.stringify(timeChanged),
|
||||
])
|
||||
const stderr = executed.stderr.toString()
|
||||
if (stderr)
|
||||
console.error(
|
||||
`Error running health check ${value.name}: ${stderr}`,
|
||||
)
|
||||
return executed.stdout.toString()
|
||||
} else {
|
||||
actionProcedure
|
||||
const moduleCode = await this.system.moduleCode
|
||||
const method = moduleCode.health?.[healthId]
|
||||
if (!method) {
|
||||
await effects.setHealth({
|
||||
name: healthId,
|
||||
status: "failure",
|
||||
message: `Expecting that thejs health check ${healthId} exists`,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
return { name, interval }
|
||||
})
|
||||
const result = await method(
|
||||
new PolyfillEffects(effects, this.system.manifest),
|
||||
timeChanged,
|
||||
)
|
||||
|
||||
if ("result" in result) {
|
||||
await effects.setHealth({
|
||||
name: healthId,
|
||||
status: "passing",
|
||||
})
|
||||
return
|
||||
}
|
||||
if ("error" in result) {
|
||||
await effects.setHealth({
|
||||
name: healthId,
|
||||
status: "failure",
|
||||
message: result.error,
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!("error-code" in result)) {
|
||||
await effects.setHealth({
|
||||
name: healthId,
|
||||
status: "failure",
|
||||
message: `Unknown error type ${JSON.stringify(result)}`,
|
||||
})
|
||||
return
|
||||
}
|
||||
const [code, message] = result["error-code"]
|
||||
if (code === 59) {
|
||||
await effects.setHealth({
|
||||
name: healthId,
|
||||
status: "disabled",
|
||||
message,
|
||||
})
|
||||
return
|
||||
}
|
||||
if (code === 60) {
|
||||
await effects.setHealth({
|
||||
name: healthId,
|
||||
status: "starting",
|
||||
message,
|
||||
})
|
||||
return
|
||||
}
|
||||
if (code === 61) {
|
||||
await effects.setHealth({
|
||||
name: healthId,
|
||||
status: "warning",
|
||||
message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
await effects.setHealth({
|
||||
name: healthId,
|
||||
status: "failure",
|
||||
message: `${result["error-code"][0]}: ${result["error-code"][1]}`,
|
||||
})
|
||||
return
|
||||
}
|
||||
}, EMBASSY_HEALTH_INTERVAL)
|
||||
|
||||
return { name: healthId, interval }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ use patch_db::json_ptr::JsonPointer;
|
||||
use rpc_toolkit::{from_fn, from_fn_async, AnyContext, Context, Empty, HandlerExt, ParentHandler};
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::disk::mount::filesystem::idmapped::IdMapped;
|
||||
use crate::disk::mount::filesystem::loop_dev::LoopDev;
|
||||
use crate::disk::mount::filesystem::overlayfs::OverlayGuard;
|
||||
use crate::prelude::*;
|
||||
@@ -26,6 +25,7 @@ use crate::status::MainStatus;
|
||||
use crate::util::clap::FromStrParser;
|
||||
use crate::util::{new_guid, Invoke};
|
||||
use crate::{db::model::ExposedUI, service::RunningStatus};
|
||||
use crate::{disk::mount::filesystem::idmapped::IdMapped, status::health_check::HealthCheckString};
|
||||
use crate::{echo, ARCH};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -605,30 +605,22 @@ async fn set_main_status(context: EffectContext, params: SetMainStatus) -> Resul
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct SetHealth {
|
||||
name: HealthCheckId,
|
||||
health_result: Option<HealthCheckResult>,
|
||||
status: HealthCheckString,
|
||||
message: Option<String>,
|
||||
}
|
||||
|
||||
async fn set_health(context: EffectContext, params: SetHealth) -> Result<Value, Error> {
|
||||
async fn set_health(
|
||||
context: EffectContext,
|
||||
SetHealth {
|
||||
name,
|
||||
status,
|
||||
message,
|
||||
}: SetHealth,
|
||||
) -> Result<Value, Error> {
|
||||
dbg!(&name);
|
||||
dbg!(&status);
|
||||
dbg!(&message);
|
||||
let context = context.deref()?;
|
||||
// TODO DrBonez + BLU-J Need to change the type from
|
||||
// ```rs
|
||||
// #[serde(tag = "result")]
|
||||
// pub enum HealthCheckResult {
|
||||
// Success,
|
||||
// Disabled,
|
||||
// Starting,
|
||||
// Loading { message: String },
|
||||
// Failure { error: String },
|
||||
// }
|
||||
// ```
|
||||
// to
|
||||
// ```ts
|
||||
// setHealth(o: {
|
||||
// name: string
|
||||
// status: HealthStatus
|
||||
// message?: string
|
||||
// }): Promise<void>
|
||||
// ```
|
||||
|
||||
let package_id = &context.id;
|
||||
context
|
||||
@@ -648,14 +640,22 @@ async fn set_health(context: EffectContext, params: SetHealth) -> Result<Value,
|
||||
match &mut main {
|
||||
&mut MainStatus::Running { ref mut health, .. }
|
||||
| &mut MainStatus::BackingUp { ref mut health, .. } => {
|
||||
health.remove(¶ms.name);
|
||||
if let SetHealth {
|
||||
health.remove(&name);
|
||||
|
||||
health.insert(
|
||||
name,
|
||||
health_result: Some(health_result),
|
||||
} = params
|
||||
{
|
||||
health.insert(name, health_result);
|
||||
}
|
||||
match status {
|
||||
HealthCheckString::Disabled => HealthCheckResult::Disabled,
|
||||
HealthCheckString::Passing => HealthCheckResult::Success,
|
||||
HealthCheckString::Starting => HealthCheckResult::Starting,
|
||||
HealthCheckString::Warning => HealthCheckResult::Loading {
|
||||
message: message.unwrap_or_default(),
|
||||
},
|
||||
HealthCheckString::Failure => HealthCheckResult::Failure {
|
||||
error: message.unwrap_or_default(),
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
_ => return Ok(()),
|
||||
};
|
||||
|
||||
@@ -22,3 +22,13 @@ impl std::fmt::Display for HealthCheckResult {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum HealthCheckString {
|
||||
Passing,
|
||||
Disabled,
|
||||
Starting,
|
||||
Warning,
|
||||
Failure,
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ export function healthCheck(o: {
|
||||
} catch (e) {
|
||||
await o.effects.setHealth({
|
||||
name: o.name,
|
||||
status: "failing",
|
||||
status: "failure",
|
||||
message: asMessage(e),
|
||||
})
|
||||
currentValue.lastResult = "failing"
|
||||
currentValue.lastResult = "failure"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -48,7 +48,7 @@ export async function checkPortListening(
|
||||
return { status: "passing", message: options.successMessage }
|
||||
}
|
||||
return {
|
||||
status: "failing",
|
||||
status: "failure",
|
||||
message: options.errorMessage,
|
||||
}
|
||||
}),
|
||||
@@ -56,7 +56,7 @@ export async function checkPortListening(
|
||||
setTimeout(
|
||||
() =>
|
||||
resolve({
|
||||
status: "failing",
|
||||
status: "failure",
|
||||
message:
|
||||
options.timeoutMessage || `Timeout trying to check port ${port}`,
|
||||
}),
|
||||
|
||||
@@ -19,14 +19,17 @@ export const checkWebUrl = async (
|
||||
} = {},
|
||||
): Promise<CheckResult> => {
|
||||
return Promise.race([fetch(url), timeoutPromise(timeout)])
|
||||
.then((x) => ({
|
||||
status: "passing" as const,
|
||||
message: successMessage,
|
||||
}))
|
||||
.then(
|
||||
(x) =>
|
||||
({
|
||||
status: "passing",
|
||||
message: successMessage,
|
||||
}) as const,
|
||||
)
|
||||
.catch((e) => {
|
||||
console.warn(`Error while fetching URL: ${url}`)
|
||||
console.error(JSON.stringify(e))
|
||||
console.error(e.toString())
|
||||
return { status: "failing" as const, message: errorMessage }
|
||||
return { status: "failure" as const, message: errorMessage }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export const runHealthScript = async (
|
||||
console.warn(errorMessage)
|
||||
console.warn(JSON.stringify(e))
|
||||
console.warn(e.toString())
|
||||
throw { status: "failing", message: errorMessage } as CheckResult
|
||||
throw { status: "failure", message: errorMessage } as CheckResult
|
||||
})
|
||||
return {
|
||||
status: "passing",
|
||||
|
||||
@@ -121,7 +121,7 @@ export class Daemons<Manifest extends SDKManifest, Ids extends string> {
|
||||
const response = await Promise.resolve(daemon.ready.fn()).catch(
|
||||
(err) =>
|
||||
({
|
||||
status: "failing",
|
||||
status: "failure",
|
||||
message: "message" in err ? err.message : String(err),
|
||||
}) as CheckResult,
|
||||
)
|
||||
|
||||
@@ -133,7 +133,12 @@ export type Daemon = {
|
||||
[DaemonProof]: never
|
||||
}
|
||||
|
||||
export type HealthStatus = "passing" | "warning" | "failing" | "disabled"
|
||||
export type HealthStatus =
|
||||
| `passing`
|
||||
| `disabled`
|
||||
| `starting`
|
||||
| `warning`
|
||||
| `failure`
|
||||
|
||||
export type SmtpValue = {
|
||||
server: string
|
||||
|
||||
Reference in New Issue
Block a user