A simple command-line tool to start NetBSD virtual machines using QEMU with sensible defaults.
1import {
2 type Kysely,
3 type Migration,
4 type MigrationProvider,
5 Migrator,
6 sql,
7} from "kysely";
8import type { Database } from "./db.ts";
9
10const migrations: Record<string, Migration> = {};
11
12const migrationProvider: MigrationProvider = {
13 // deno-lint-ignore require-await
14 async getMigrations() {
15 return migrations;
16 },
17};
18
19migrations["001"] = {
20 async up(db: Kysely<unknown>): Promise<void> {
21 await db.schema
22 .createTable("virtual_machines")
23 .addColumn("id", "varchar", (col) => col.primaryKey())
24 .addColumn("name", "varchar", (col) => col.notNull().unique())
25 .addColumn("bridge", "varchar")
26 .addColumn("macAddress", "varchar", (col) => col.notNull().unique())
27 .addColumn("memory", "varchar", (col) => col.notNull())
28 .addColumn("cpus", "integer", (col) => col.notNull())
29 .addColumn("cpu", "varchar", (col) => col.notNull())
30 .addColumn("diskSize", "varchar", (col) => col.notNull())
31 .addColumn("drivePath", "varchar")
32 .addColumn("version", "varchar", (col) => col.notNull())
33 .addColumn("diskFormat", "varchar")
34 .addColumn("isoPath", "varchar")
35 .addColumn("status", "varchar", (col) => col.notNull())
36 .addColumn("pid", "integer")
37 .addColumn(
38 "createdAt",
39 "varchar",
40 (col) => col.notNull().defaultTo(sql`CURRENT_TIMESTAMP`),
41 )
42 .addColumn(
43 "updatedAt",
44 "varchar",
45 (col) => col.notNull().defaultTo(sql`CURRENT_TIMESTAMP`),
46 )
47 .execute();
48 },
49
50 async down(db: Kysely<unknown>): Promise<void> {
51 await db.schema.dropTable("virtual_machines").execute();
52 },
53};
54
55migrations["002"] = {
56 async up(db: Kysely<unknown>): Promise<void> {
57 await db.schema
58 .alterTable("virtual_machines")
59 .addColumn("portForward", "varchar")
60 .execute();
61 },
62
63 async down(db: Kysely<unknown>): Promise<void> {
64 await db.schema
65 .alterTable("virtual_machines")
66 .dropColumn("portForward")
67 .execute();
68 },
69};
70
71migrations["003"] = {
72 async up(db: Kysely<unknown>): Promise<void> {
73 await db.schema
74 .createTable("images")
75 .addColumn("id", "varchar", (col) => col.primaryKey())
76 .addColumn("repository", "varchar", (col) => col.notNull())
77 .addColumn("tag", "varchar", (col) => col.notNull())
78 .addColumn("size", "integer", (col) => col.notNull())
79 .addColumn("path", "varchar", (col) => col.notNull())
80 .addColumn("createdAt", "varchar", (col) => col.notNull())
81 .execute();
82 },
83
84 async down(db: Kysely<unknown>): Promise<void> {
85 await db.schema.dropTable("images").execute();
86 },
87};
88
89migrations["004"] = {
90 async up(db: Kysely<unknown>): Promise<void> {
91 await db.schema
92 .alterTable("images")
93 .addColumn("format", "varchar", (col) => col.notNull().defaultTo("qcow2"))
94 .execute();
95 },
96
97 async down(db: Kysely<unknown>): Promise<void> {
98 await db.schema
99 .alterTable("images")
100 .dropColumn("format")
101 .execute();
102 },
103};
104
105migrations["005"] = {
106 async up(db: Kysely<unknown>): Promise<void> {
107 await db.schema
108 .createTable("images_new")
109 .addColumn("id", "varchar", (col) => col.primaryKey())
110 .addColumn("repository", "varchar", (col) => col.notNull())
111 .addColumn("tag", "varchar", (col) => col.notNull())
112 .addColumn("size", "integer", (col) => col.notNull())
113 .addColumn("path", "varchar", (col) => col.notNull())
114 .addColumn("format", "varchar", (col) => col.notNull().defaultTo("qcow2"))
115 .addColumn("createdAt", "varchar", (col) => col.notNull())
116 .addUniqueConstraint("images_repository_tag_unique", [
117 "repository",
118 "tag",
119 ])
120 .execute();
121
122 await sql`
123 INSERT INTO images_new (id, repository, tag, size, path, format, createdAt)
124 SELECT id, repository, tag, size, path, format, createdAt FROM images
125 `.execute(db);
126
127 await db.schema.dropTable("images").execute();
128 await sql`ALTER TABLE images_new RENAME TO images`.execute(db);
129 },
130
131 async down(db: Kysely<unknown>): Promise<void> {
132 await db.schema
133 .createTable("images_old")
134 .addColumn("id", "varchar", (col) => col.primaryKey())
135 .addColumn("repository", "varchar", (col) => col.notNull())
136 .addColumn("tag", "varchar", (col) => col.notNull())
137 .addColumn("size", "integer", (col) => col.notNull())
138 .addColumn("path", "varchar", (col) => col.notNull())
139 .addColumn("format", "varchar", (col) => col.notNull().defaultTo("qcow2"))
140 .addColumn("createdAt", "varchar", (col) => col.notNull())
141 .execute();
142
143 await sql`
144 INSERT INTO images_old (id, repository, tag, size, path, format, createdAt)
145 SELECT id, repository, tag, size, path, format, createdAt FROM images
146 `.execute(db);
147
148 await db.schema.dropTable("images").execute();
149 await sql`ALTER TABLE images_old RENAME TO images`.execute(db);
150 },
151};
152
153migrations["006"] = {
154 async up(db: Kysely<unknown>): Promise<void> {
155 await db.schema
156 .createTable("images_new")
157 .addColumn("id", "varchar", (col) => col.primaryKey())
158 .addColumn("repository", "varchar", (col) => col.notNull())
159 .addColumn("tag", "varchar", (col) => col.notNull())
160 .addColumn("size", "integer", (col) => col.notNull())
161 .addColumn("path", "varchar", (col) => col.notNull())
162 .addColumn("format", "varchar", (col) => col.notNull().defaultTo("qcow2"))
163 .addColumn(
164 "createdAt",
165 "varchar",
166 (col) => col.notNull().defaultTo(sql`CURRENT_TIMESTAMP`),
167 )
168 .addUniqueConstraint("images_repository_tag_unique", [
169 "repository",
170 "tag",
171 ])
172 .execute();
173
174 await sql`
175 INSERT INTO images_new (id, repository, tag, size, path, format, createdAt)
176 SELECT id, repository, tag, size, path, format, createdAt FROM images
177 `.execute(db);
178
179 await db.schema.dropTable("images").execute();
180 await sql`ALTER TABLE images_new RENAME TO images`.execute(db);
181 },
182
183 async down(db: Kysely<unknown>): Promise<void> {
184 await db.schema
185 .createTable("images_old")
186 .addColumn("id", "varchar", (col) => col.primaryKey())
187 .addColumn("repository", "varchar", (col) => col.notNull())
188 .addColumn("tag", "varchar", (col) => col.notNull())
189 .addColumn("size", "integer", (col) => col.notNull())
190 .addColumn("path", "varchar", (col) => col.notNull())
191 .addColumn("format", "varchar", (col) => col.notNull().defaultTo("qcow2"))
192 .addColumn("createdAt", "varchar", (col) => col.notNull())
193 .addUniqueConstraint("images_repository_tag_unique", [
194 "repository",
195 "tag",
196 ])
197 .execute();
198
199 await sql`
200 INSERT INTO images_old (id, repository, tag, size, path, format, createdAt)
201 SELECT id, repository, tag, size, path, format, createdAt FROM images
202 `.execute(db);
203 },
204};
205
206migrations["007"] = {
207 async up(db: Kysely<unknown>): Promise<void> {
208 await db.schema
209 .alterTable("images")
210 .addColumn("digest", "varchar")
211 .execute();
212 },
213 async down(db: Kysely<unknown>): Promise<void> {
214 await db.schema
215 .alterTable("images")
216 .dropColumn("digest")
217 .execute();
218 },
219};
220
221migrations["008"] = {
222 async up(db: Kysely<unknown>): Promise<void> {
223 await db.schema
224 .createTable("volumes")
225 .addColumn("id", "varchar", (col) => col.primaryKey())
226 .addColumn("name", "varchar", (col) => col.notNull().unique())
227 .addColumn(
228 "baseImageId",
229 "varchar",
230 (col) => col.notNull().references("images.id").onDelete("cascade"),
231 )
232 .addColumn("path", "varchar", (col) => col.notNull())
233 .addColumn(
234 "createdAt",
235 "varchar",
236 (col) => col.notNull().defaultTo(sql`CURRENT_TIMESTAMP`),
237 )
238 .execute();
239 },
240
241 async down(db: Kysely<unknown>): Promise<void> {
242 await db.schema.dropTable("volumes").execute();
243 },
244};
245
246migrations["009"] = {
247 async up(db: Kysely<unknown>): Promise<void> {
248 await db.schema
249 .createTable("volumes_new")
250 .addColumn("id", "varchar", (col) => col.primaryKey())
251 .addColumn("name", "varchar", (col) => col.notNull().unique())
252 .addColumn(
253 "baseImageId",
254 "varchar",
255 (col) => col.notNull().references("images.id").onDelete("cascade"),
256 )
257 .addColumn("path", "varchar", (col) => col.notNull())
258 .addColumn(
259 "createdAt",
260 "varchar",
261 (col) => col.notNull().defaultTo(sql`CURRENT_TIMESTAMP`),
262 )
263 .execute();
264
265 await sql`
266 INSERT INTO volumes_new (id, name, baseImageId, path, createdAt)
267 SELECT id, name, baseImageId, path, createdAt FROM volumes
268 `.execute(db);
269
270 await db.schema.dropTable("volumes").execute();
271 await sql`ALTER TABLE volumes_new RENAME TO volumes`.execute(db);
272 },
273
274 async down(db: Kysely<unknown>): Promise<void> {
275 await db.schema.dropTable("volumes").execute();
276 },
277};
278
279migrations["010"] = {
280 async up(db: Kysely<unknown>): Promise<void> {
281 await db.schema
282 .alterTable("virtual_machines")
283 .addColumn("volume", "varchar")
284 .execute();
285 },
286
287 async down(db: Kysely<unknown>): Promise<void> {
288 await db.schema
289 .alterTable("virtual_machines")
290 .dropColumn("volume")
291 .execute();
292 },
293};
294
295export const migrateToLatest = async (db: Database): Promise<void> => {
296 const migrator = new Migrator({ db, provider: migrationProvider });
297 const { error } = await migrator.migrateToLatest();
298 if (error) throw error;
299};