A convenient CLI tool to quickly spin up DragonflyBSD virtual machines using QEMU with sensible defaults.
1import { Data, Effect } from "effect";
2import type { DeleteResult, InsertResult, UpdateResult } from "kysely";
3import { ctx } from "./context.ts";
4import type { VirtualMachine } from "./db.ts";
5import type { STATUS } from "./types.ts";
6
7export class DbError extends Data.TaggedError("DatabaseError")<{
8 cause?: unknown;
9}> {}
10
11export class InstanceNotFoundError
12 extends Data.TaggedError("InstanceNotFoundError")<{
13 name: string;
14 message?: string;
15 }> {}
16
17export const saveInstanceState = (
18 vm: VirtualMachine,
19): Effect.Effect<InsertResult[], DbError, never> =>
20 Effect.tryPromise({
21 try: () =>
22 ctx.db.insertInto("virtual_machines")
23 .values(vm)
24 .execute(),
25 catch: (error) => new DbError({ cause: error }),
26 });
27
28export const updateInstanceState = (
29 name: string,
30 status: STATUS,
31 pid?: number,
32): Effect.Effect<UpdateResult[], DbError, never> =>
33 Effect.tryPromise({
34 try: () =>
35 ctx.db.updateTable("virtual_machines")
36 .set({
37 status,
38 pid,
39 updatedAt: new Date().toISOString(),
40 })
41 .where((eb) =>
42 eb.or([
43 eb("name", "=", name),
44 eb("id", "=", name),
45 ])
46 )
47 .execute(),
48 catch: (error) => new DbError({ cause: error }),
49 });
50
51export const removeInstanceState = (
52 name: string,
53): Effect.Effect<DeleteResult[], DbError, never> =>
54 Effect.tryPromise({
55 try: () =>
56 ctx.db.deleteFrom("virtual_machines")
57 .where((eb) =>
58 eb.or([
59 eb("name", "=", name),
60 eb("id", "=", name),
61 ])
62 )
63 .execute(),
64 catch: (error) => new DbError({ cause: error }),
65 });
66
67export const getInstanceState = (
68 name: string,
69): Effect.Effect<VirtualMachine | undefined, DbError, never> =>
70 Effect.tryPromise({
71 try: () =>
72 ctx.db.selectFrom("virtual_machines")
73 .selectAll()
74 .where((eb) =>
75 eb.or([
76 eb("name", "=", name),
77 eb("id", "=", name),
78 ])
79 )
80 .executeTakeFirst(),
81 catch: (error) => new DbError({ cause: error }),
82 });
83
84export const getInstanceStateOrFail = (name: string) =>
85 Effect.flatMap(
86 getInstanceState(name),
87 (vm) =>
88 vm ? Effect.succeed(vm) : Effect.fail(
89 new InstanceNotFoundError({
90 name,
91 message: `Instance not found: ${name}`,
92 }),
93 ),
94 );
95
96export const listInstances = (
97 all: boolean,
98): Effect.Effect<VirtualMachine[], DbError, never> =>
99 Effect.tryPromise({
100 try: () =>
101 ctx.db.selectFrom("virtual_machines")
102 .selectAll()
103 .where((eb) => {
104 if (all) {
105 return eb("id", "!=", "");
106 }
107 return eb("status", "=", "RUNNING");
108 })
109 .execute(),
110 catch: (error) => new DbError({ cause: error }),
111 });