| 1 | /** |
| 2 | * The machine catalogue: every label a workflow job can ask for in |
| 3 | * `runs-on`, and the machine g1t runs it on. Mirrors |
| 4 | * `g1t_contracts::actions::INSTANCE_TYPES` row for row (a Rust test reads |
| 5 | * this file), and services/runner/wrangler.jsonc gives each row's class its |
| 6 | * instance type (a runner test holds them together). Each machine is a |
| 7 | * named Cloudflare Containers instance type: the standard machine is |
| 8 | * standard-3 (2 vCPU, 8 GiB, 16 GB of disk), `g1t-2core` is kept as a name |
| 9 | * of it so workflows that ask for it keep working, and `g1t-4core` is |
| 10 | * standard-4 (4 vCPU, 12 GiB, 20 GB). See CONTRIBUTING.md, "Adding a |
| 11 | * machine size". |
| 12 | */ |
| 13 | export type InstanceType = { |
| 14 | /** The `runs-on` label, or `standard` for the default. */ |
| 15 | label: string; |
| 16 | /** |
| 17 | * The machine, as it is billed and reported (`2-core`): runner minutes |
| 18 | * are counted by machine, so a label renamed or pointed at another |
| 19 | * machine leaves the history where it was. Never reused. |
| 20 | */ |
| 21 | machine: string; |
| 22 | /** The runner's Durable Object class that runs it, one per label. */ |
| 23 | sandboxClass: string; |
| 24 | /** The named Containers instance type its class runs on. */ |
| 25 | container: string; |
| 26 | vcpu: number; |
| 27 | memoryGib: number; |
| 28 | diskGb: number; |
| 29 | /** |
| 30 | * What a minute of it is charged, in millionths of a dollar: a job is |
| 31 | * charged its minutes, rounded up, at this rate (`runnerMinutes`). |
| 32 | */ |
| 33 | microsPerMinute: number; |
| 34 | }; |
| 35 | |
| 36 | /** Every label a job can ask for, the default first. One row a line, as the Rust table's test reads them. */ |
| 37 | export const INSTANCE_TYPES: readonly InstanceType[] = [ |
| 38 | { label: "standard", machine: "2-core", sandboxClass: "AttemptSandbox", container: "standard-3", vcpu: 2, memoryGib: 8, diskGb: 16, microsPerMinute: 4000 }, |
| 39 | // The standard machine under the name it had when it was larger. |
| 40 | { label: "g1t-2core", machine: "2-core", sandboxClass: "Sandbox2Core", container: "standard-3", vcpu: 2, memoryGib: 8, diskGb: 16, microsPerMinute: 4000 }, |
| 41 | { label: "g1t-4core", machine: "4-core", sandboxClass: "Sandbox4Core", container: "standard-4", vcpu: 4, memoryGib: 12, diskGb: 20, microsPerMinute: 7000 }, |
| 42 | ]; |
| 43 | |
| 44 | /** The default: what `ubuntu-latest` and every other hosted label get. */ |
| 45 | export const STANDARD_INSTANCE: InstanceType = INSTANCE_TYPES[0]!; |
| 46 | |
| 47 | /** An instance type by its label; the standard one for anything else. */ |
| 48 | export function instanceNamed(label: string | null | undefined): InstanceType { |
| 49 | const wanted = label?.trim().toLowerCase(); |
| 50 | return INSTANCE_TYPES.find((instance) => instance.label === wanted) ?? STANDARD_INSTANCE; |
| 51 | } |
| 52 | |
| 53 | /** The machines, once each, in the catalogue's order: the first row of each. */ |
| 54 | export function machines(): InstanceType[] { |
| 55 | return INSTANCE_TYPES.filter((instance, at) => INSTANCE_TYPES.findIndex((other) => other.machine === instance.machine) === at); |
| 56 | } |
| 57 | |
| 58 | /** A machine by its key (`2-core`); null when there is none. */ |
| 59 | export function machineNamed(machine: string): InstanceType | null { |
| 60 | return INSTANCE_TYPES.find((instance) => instance.machine === machine) ?? null; |
| 61 | } |
| 62 | |
| 63 | /** Its price as a multiple of the standard machine's. */ |
| 64 | export function priceScale(instance: InstanceType): number { |
| 65 | return instance.microsPerMinute / STANDARD_INSTANCE.microsPerMinute; |
| 66 | } |
| 67 | |
| 68 | /** `2 vCPU, 8 GiB, 16 GB`. */ |
| 69 | export function machineSize(instance: InstanceType): string { |
| 70 | return `${instance.vcpu} vCPU, ${instance.memoryGib} GiB, ${instance.diskGb} GB`; |
| 71 | } |
| 72 | |
| 73 | /** The minutes a job is charged for: its time rounded up to the minute, at least 1 for a job that ran. */ |
| 74 | export function runnerMinutes(seconds: number): number { |
| 75 | return seconds <= 0 ? 0 : Math.ceil(seconds / 60); |
| 76 | } |