(os: VmOs, arch: VmArch = "x64")
| 236 | }; |
| 237 | |
| 238 | export const ec2Vm = (os: VmOs, arch: VmArch = "x64"): VmProvider => ({ |
| 239 | os, |
| 240 | provision: async () => { |
| 241 | const user = guestUser(os); |
| 242 | // A throwaway SSH keypair, authorized via user-data (no EC2 key pair needed — |
| 243 | // we drive over OpenSSH key auth, not the Windows password). |
| 244 | const keyDir = mkdtempSync(join(tmpdir(), "executor-ec2-")); |
| 245 | const keyPath = join(keyDir, "id"); |
| 246 | await execFileP("ssh-keygen", ["-t", "ed25519", "-N", "", "-q", "-f", keyPath]); |
| 247 | chmodSync(keyPath, 0o600); |
| 248 | const publicKey = (await execFileP("ssh-keygen", ["-y", "-f", keyPath])).stdout.trim(); |
| 249 | |
| 250 | const [myIp, ami, subnet] = await Promise.all([egressIp(), latestAmi(os), defaultSubnet()]); |
| 251 | const sg = await ensureSecurityGroup(myIp); |
| 252 | const userData = os === "windows" ? windowsUserData(publicKey) : linuxUserData(publicKey); |
| 253 | const userDataFile = join(keyDir, "user-data.txt"); |
| 254 | writeFileSync(userDataFile, userData); |
| 255 | |
| 256 | const instanceId = await aws([ |
| 257 | "ec2", |
| 258 | "run-instances", |
| 259 | "--image-id", |
| 260 | ami, |
| 261 | "--instance-type", |
| 262 | INSTANCE_TYPE, |
| 263 | "--count", |
| 264 | "1", |
| 265 | "--security-group-ids", |
| 266 | sg, |
| 267 | "--subnet-id", |
| 268 | subnet, |
| 269 | "--associate-public-ip-address", |
| 270 | "--instance-initiated-shutdown-behavior", |
| 271 | "terminate", |
| 272 | "--user-data", |
| 273 | `file://${userDataFile}`, |
| 274 | "--tag-specifications", |
| 275 | `ResourceType=instance,Tags=[{Key=Name,Value=${TAG}-${os}},{Key=purpose,Value=e2e}]`, |
| 276 | "--query", |
| 277 | "Instances[0].InstanceId", |
| 278 | ]); |
| 279 | |
| 280 | let ip = ""; |
| 281 | const tunnelClosers: Array<() => void> = []; |
| 282 | |
| 283 | const ssh = async (command: string): Promise<SshResult> => { |
| 284 | try { |
| 285 | const { stdout, stderr } = await execFileP( |
| 286 | "ssh", |
| 287 | ["-i", keyPath, ...SSH_OPTS, `${user}@${ip}`, command], |
| 288 | { maxBuffer: 64 * 1024 * 1024 }, |
| 289 | ); |
| 290 | return { stdout, stderr, code: 0 }; |
| 291 | } catch (err) { |
| 292 | const e = err as { stdout?: string; stderr?: string; code?: number }; |
| 293 | return { |
| 294 | stdout: e.stdout ?? "", |
| 295 | stderr: e.stderr ?? "", |
no test coverage detected