feat: support AWS target userdata in worker

This commit is contained in:
Peter Steinberger 2026-05-04 02:23:58 +01:00
parent ab7fc6fd71
commit 343ca7baa9
No known key found for this signature in database
3 changed files with 38 additions and 13 deletions

View File

@ -1,9 +1,9 @@
import { AwsClient } from "aws4fetch";
import { XMLParser } from "fast-xml-parser";
import { cloudInit } from "./bootstrap";
import { awsUserData } from "./bootstrap";
import {
awsInstanceTypeCandidatesForClass,
awsInstanceTypeCandidatesForTargetClass,
sshPorts,
validCIDRs,
type LeaseConfig,
@ -327,7 +327,7 @@ export class EC2SpotClient {
KeyName: config.providerKey,
MaxCount: "1",
MinCount: "1",
UserData: btoa(cloudInit(config)),
UserData: btoa(awsUserData(config)),
"BlockDeviceMapping.1.DeviceName": "/dev/sda1",
"BlockDeviceMapping.1.Ebs.DeleteOnTermination": "true",
"BlockDeviceMapping.1.Ebs.Encrypted": "true",
@ -351,6 +351,14 @@ export class EC2SpotClient {
} else {
params["SecurityGroupId.1"] = securityGroupID;
}
if (config.target === "macos") {
const hostID = config.awsMacHostID || this.env.CRABBOX_AWS_MAC_HOST_ID || "";
if (!hostID) {
throw new Error("aws target=macos requires CRABBOX_AWS_MAC_HOST_ID");
}
params["Placement.HostId"] = hostID;
params["Placement.Tenancy"] = "host";
}
addRunInstancesTagSpecifications(params, { ...labels, Name: name }, config.capacityMarket);
const root = await this.ec2("RunInstances", params);
const instance = items(record(root["instancesSet"])["item"])[0];
@ -364,6 +372,15 @@ export class EC2SpotClient {
if (config.awsAMI || this.env.CRABBOX_AWS_AMI) {
return config.awsAMI || this.env.CRABBOX_AWS_AMI || "";
}
if (config.target === "windows") {
return "resolve:ssm:/aws/service/ami-windows-latest/Windows_Server-2022-English-Full-Base";
}
if (config.target === "macos") {
if (config.serverType.startsWith("mac1.")) {
return "resolve:ssm:/aws/service/ec2-macos/sonoma/x86_64_mac/latest/image_id";
}
return "resolve:ssm:/aws/service/ec2-macos/sonoma/arm64_mac/latest/image_id";
}
const root = await this.ec2("DescribeImages", {
"Owner.1": awsUbuntuOwner,
"Filter.1.Name": "architecture",
@ -631,15 +648,22 @@ function asString(value: unknown): string {
}
export function awsLaunchCandidates(
config: Pick<LeaseConfig, "serverType" | "serverTypeExplicit" | "class">,
config: Pick<LeaseConfig, "serverType" | "serverTypeExplicit" | "class" | "target">,
): string[] {
if (config.serverTypeExplicit) {
return [config.serverType];
}
if (config.target === "macos") {
return uniqueStrings([
config.serverType,
...awsInstanceTypeCandidatesForTargetClass(config.target, config.class),
]);
}
const policyFallback = config.target === "windows" ? "t3.large" : "t3.small";
return uniqueStrings([
config.serverType,
...awsInstanceTypeCandidatesForClass(config.class),
...awsPolicyFallbackCandidates(),
...awsInstanceTypeCandidatesForTargetClass(config.target, config.class),
policyFallback,
]);
}
@ -680,10 +704,6 @@ export function awsQuotaPreflightAttempt(
};
}
function awsPolicyFallbackCandidates(): string[] {
return ["t3.small"];
}
function uniqueStrings(values: string[]): string[] {
return [...new Set(values.filter(Boolean))];
}

View File

@ -332,3 +332,11 @@ function optionalBootstrap(config: LeaseConfig): string {
}
return parts.join("\n");
}
function psQuote(value: string): string {
return `'${value.replaceAll("'", "''")}'`;
}
function shellQuote(value: string): string {
return `'${value.replaceAll("'", "'\\''")}'`;
}

View File

@ -58,9 +58,6 @@ export function leaseConfig(input: LeaseRequest): LeaseConfig {
if (provider !== "aws") {
throw new Error(`unsupported target for brokered ${provider}: ${target}`);
}
if (!input.awsMacHostID?.trim()) {
throw new Error("brokered aws target=macos requires awsMacHostID or CRABBOX_AWS_MAC_HOST_ID");
}
if ((input.capacity?.market ?? "spot") !== "on-demand") {
throw new Error("brokered aws target=macos requires capacity.market=on-demand");
}