feat: Add the stop/start loop for the service

This commit is contained in:
J H
2024-03-06 10:55:21 -07:00
parent 093a5d4ddf
commit 8410929e86
5 changed files with 52 additions and 13 deletions

View File

@@ -86,10 +86,11 @@ export class MainLoop {
public async clean(options?: { timeout?: number }) {
const { mainEvent, healthLoops, propertiesEvent } = this
const main = await mainEvent
delete this.mainEvent
delete this.healthLoops
delete this.propertiesEvent
if (mainEvent) await (await mainEvent).daemon.term()
if (mainEvent) await main?.daemon.term()
clearInterval(propertiesEvent)
if (healthLoops) healthLoops.forEach((x) => clearInterval(x.interval))
}

View File

@@ -7,7 +7,7 @@ use std::{ffi::OsString, time::Instant};
use chrono::Utc;
use clap::builder::{TypedValueParser, ValueParserFactory};
use clap::Parser;
use imbl_value::json;
use imbl_value::{json, InternedString};
use models::{ActionId, HealthCheckId, ImageId, PackageId};
use patch_db::json_ptr::JsonPointer;
use rpc_toolkit::{from_fn, from_fn_async, AnyContext, Context, Empty, HandlerExt, ParentHandler};
@@ -111,11 +111,15 @@ pub fn service_effect_handler() -> ParentHandler {
.subcommand(
"createOverlayedImage",
from_fn_async(create_overlayed_image)
.with_custom_display_fn::<AnyContext, _>(|_, path| {
.with_custom_display_fn::<AnyContext, _>(|_, (path, _)| {
Ok(println!("{}", path.display()))
})
.with_remote_cli::<ContainerCliContext>(),
)
.subcommand(
"destroyOverlayedImage",
from_fn_async(destroy_overlayed_image).no_cli(),
)
.subcommand(
"getSslCertificate",
from_fn_async(get_ssl_certificate).no_cli(),
@@ -668,7 +672,32 @@ async fn set_health(context: EffectContext, params: SetHealth) -> Result<Value,
.await?;
Ok(json!(()))
}
#[derive(serde::Deserialize, serde::Serialize, Parser)]
#[serde(rename_all = "camelCase")]
#[command(rename_all = "camelCase")]
pub struct DestroyOverlayedImageParams {
image_id: ImageId,
guid: InternedString,
}
#[instrument(skip_all)]
pub async fn destroy_overlayed_image(
ctx: EffectContext,
DestroyOverlayedImageParams { image_id, guid }: DestroyOverlayedImageParams,
) -> Result<(), Error> {
let ctx = ctx.deref()?;
if ctx
.persistent_container
.overlays
.lock()
.await
.remove(&guid)
.is_none()
{
tracing::warn!("Could not find a guard to remove on the destroy overlayed image; assumming that it already is removed and will be skipping");
}
Ok(())
}
#[derive(serde::Deserialize, serde::Serialize, Parser)]
#[serde(rename_all = "camelCase")]
#[command(rename_all = "camelCase")]
@@ -680,10 +709,10 @@ pub struct CreateOverlayedImageParams {
pub async fn create_overlayed_image(
ctx: EffectContext,
CreateOverlayedImageParams { image_id }: CreateOverlayedImageParams,
) -> Result<PathBuf, Error> {
) -> Result<(PathBuf, InternedString), Error> {
let ctx = ctx.deref()?;
let path = Path::new("images")
.join(&*ARCH)
.join(*ARCH)
.join(&image_id)
.with_extension("squashfs");
if let Some(image) = ctx
@@ -730,7 +759,7 @@ pub async fn create_overlayed_image(
.lock()
.await
.insert(guid.clone(), guard);
Ok(container_mountpoint)
Ok((container_mountpoint, guid))
} else {
Err(Error::new(
eyre!("image {image_id} not found in s9pk"),

View File

@@ -276,7 +276,13 @@ export type Effects = {
}): Promise<unknown>
/** A low level api used by makeOverlay */
createOverlayedImage(options: { imageId: string }): Promise<string>
createOverlayedImage(options: { imageId: string }): Promise<[string, string]>
/** A low level api used by destroyOverlay + makeOverlay:destroy */
destroyOverlayedImage(options: {
imageId: string
guid: string
}): Promise<void>
/** Removes all network bindings */
clearBindings(): Promise<void>

View File

@@ -10,9 +10,10 @@ export class Overlay {
readonly effects: T.Effects,
readonly imageId: string,
readonly rootfs: string,
readonly guid: string,
) {}
static async of(effects: T.Effects, imageId: string) {
const rootfs = await effects.createOverlayedImage({ imageId })
const [rootfs, guid] = await effects.createOverlayedImage({ imageId })
for (const dirPart of ["dev", "sys", "proc", "run"] as const) {
await fs.mkdir(`${rootfs}/${dirPart}`, { recursive: true })
@@ -23,7 +24,7 @@ export class Overlay {
])
}
return new Overlay(effects, imageId, rootfs)
return new Overlay(effects, imageId, rootfs, guid)
}
async mount(options: MountOptions, path: string): Promise<Overlay> {
@@ -51,8 +52,9 @@ export class Overlay {
}
async destroy() {
await execFile("umount", ["-R", this.rootfs])
await fs.rm(this.rootfs, { recursive: true, force: true })
const imageId = this.imageId
const guid = this.guid
await this.effects.destroyOverlayedImage({ imageId, guid })
}
async exec(

View File

@@ -246,7 +246,7 @@ export const createUtils = <
console.error(data.toString())
})
childProcess.on("close", (code: any) => {
childProcess.on("exit", (code: any) => {
if (code === 0) {
return resolve(null)
}
@@ -262,7 +262,7 @@ export const createUtils = <
try {
childProcess.kill(signal)
if (timeout <= NO_TIMEOUT) {
if (timeout > NO_TIMEOUT) {
const didTimeout = await Promise.race([
new Promise((resolve) => setTimeout(resolve, timeout)).then(
() => true,
@@ -270,6 +270,7 @@ export const createUtils = <
answer.then(() => false),
])
if (didTimeout) childProcess.kill(SIGKILL)
return
}
await answer
} finally {