Files
orca/src/shared/plugins/plugin-vm-recipe-artifact.ts
T

66 lines
2.3 KiB
TypeScript

import { z } from 'zod'
import { ORCA_VM_RECIPE_ID_PATTERN, ORCA_VM_RECIPE_ID_RULE } from '../orca-yaml'
import type { OrcaVmRecipe } from '../types'
const recipeCommandSchema = z
.string()
.trim()
.min(1)
.max(32 * 1024)
.refine((value) => !value.includes('\0'), 'must not contain NUL bytes')
const pluginVmRecipeArtifactSchema = z
.object({
schemaVersion: z.literal(1),
id: z.string().regex(ORCA_VM_RECIPE_ID_PATTERN, ORCA_VM_RECIPE_ID_RULE),
name: z.string().trim().min(1).max(128),
description: z.string().trim().min(1).max(1024).optional(),
create: recipeCommandSchema,
suspend: recipeCommandSchema.optional(),
resume: recipeCommandSchema.optional(),
destroy: z.union([recipeCommandSchema, z.literal('none')]).optional()
})
.strict()
.superRefine((recipe, ctx) => {
if (Boolean(recipe.suspend) !== Boolean(recipe.resume)) {
ctx.addIssue({
code: 'custom',
path: recipe.suspend ? ['resume'] : ['suspend'],
message: 'suspend and resume must be declared together'
})
}
})
export type PluginVmRecipeCommand = {
phase: 'create' | 'suspend' | 'resume' | 'destroy'
command: string
}
export function parsePluginVmRecipeArtifact(raw: string): OrcaVmRecipe {
const parsed = pluginVmRecipeArtifactSchema.parse(JSON.parse(raw))
const destroyDisabled = parsed.destroy === 'none'
return {
id: parsed.id,
name: parsed.name,
create: parsed.create,
...(parsed.description ? { description: parsed.description } : {}),
...(parsed.suspend ? { suspend: parsed.suspend } : {}),
...(parsed.resume ? { resume: parsed.resume } : {}),
...(parsed.destroy && !destroyDisabled ? { destroy: parsed.destroy } : {}),
...(destroyDisabled ? { destroyDisabled: true } : {})
}
}
export function listPluginVmRecipeCommands(recipe: OrcaVmRecipe): PluginVmRecipeCommand[] {
return [
{ phase: 'create', command: recipe.create },
...(recipe.suspend ? [{ phase: 'suspend' as const, command: recipe.suspend }] : []),
...(recipe.resume ? [{ phase: 'resume' as const, command: recipe.resume }] : []),
...(recipe.destroy
? [{ phase: 'destroy' as const, command: recipe.destroy }]
: recipe.destroyDisabled
? [{ phase: 'destroy' as const, command: 'none' }]
: [])
]
}