"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var ec2_exports = {}; __export(ec2_exports, { createPreviewSecurityGroup: () => createPreviewSecurityGroup, deleteKeyPairAws: () => deleteKeyPairAws, deleteSecurityGroupAws: () => deleteSecurityGroupAws, describeAllManagedInstances: () => describeAllManagedInstances, generateAndImportKeyPair: () => generateAndImportKeyPair, generateSshKeyPair: () => generateSshKeyPair, launchInstance: () => launchInstance, makeEc2Client: () => makeEc2Client, makeStsClient: () => makeStsClient, terminateInstance: () => terminateInstance, validateAwsCredentials: () => validateAwsCredentials, waitForInstanceRunning: () => waitForInstanceRunning }); module.exports = __toCommonJS(ec2_exports); var import_client_ec2 = require("@aws-sdk/client-ec2"); var import_client_sts = require("@aws-sdk/client-sts"); var import_crypto = require("crypto"); var import_logger = require("../lib/logger"); var import_encryption = require("../lib/encryption"); const log = (0, import_logger.createLogger)("EC2"); const UBUNTU_22_04_AMI = { "us-east-1": "ami-0e86e20dae9224db8", "us-east-2": "ami-0a0d9cf81c479446a", "us-west-1": "ami-05c969369880fa2c2", "us-west-2": "ami-03f8acd418785369b", "eu-west-1": "ami-0694d931cee176e7d", "eu-west-2": "ami-0f3d9639a5674d559", "eu-west-3": "ami-022e307f4b9e39f45", "eu-central-1": "ami-0faab6bdbac9486fb", "ap-southeast-1": "ami-0823c236601fef765", "ap-southeast-2": "ami-07620139298af599e", "ap-northeast-1": "ami-0b7546e839d7ace12", "ap-northeast-2": "ami-042e76978adeb8c48", "ap-south-1": "ami-076e3a557efe1aa9c", "sa-east-1": "ami-0eed58016fbe42de3", "ca-central-1": "ami-024f768de9e73d4f4", "eu-north-1": "ami-00381a880aa48c6c6", "me-south-1": "ami-09574f34b8dcd2eac", "af-south-1": "ami-08fdcf06b39fe83ec" }; function makeEc2Client(user) { const accessKeyId = user.awsAccessKeyId ? (0, import_encryption.decrypt)(user.awsAccessKeyId) : ""; const secretAccessKey = user.awsSecretAccessKey ? (0, import_encryption.decrypt)(user.awsSecretAccessKey) : ""; return new import_client_ec2.EC2Client({ region: user.awsRegion, credentials: { accessKeyId, secretAccessKey } }); } function makeStsClient(user) { const accessKeyId = user.awsAccessKeyId ? (0, import_encryption.decrypt)(user.awsAccessKeyId) : ""; const secretAccessKey = user.awsSecretAccessKey ? (0, import_encryption.decrypt)(user.awsSecretAccessKey) : ""; return new import_client_sts.STSClient({ region: user.awsRegion, credentials: { accessKeyId, secretAccessKey } }); } async function validateAwsCredentials(user) { try { const sts = makeStsClient(user); const res = await sts.send(new import_client_sts.GetCallerIdentityCommand({})); return { success: true, arn: res.Arn }; } catch (e) { return { success: false, error: e.message }; } } function generateSshKeyPair() { const { privateKey: privKeyPem, publicKey: pubKeyPem } = (0, import_crypto.generateKeyPairSync)("rsa", { modulusLength: 2048, publicKeyEncoding: { type: "spki", format: "pem" }, privateKeyEncoding: { type: "pkcs1", format: "pem" } }); const pubKeyObj = (0, import_crypto.createPublicKey)(pubKeyPem); const pubKeyDer = pubKeyObj.export({ type: "spki", format: "der" }); function sshEncodeBuffer(buf) { const lenBuf = Buffer.allocUnsafe(4); lenBuf.writeUInt32BE(buf.length, 0); return Buffer.concat([lenBuf, buf]); } const keyTypeStr = Buffer.from("ssh-rsa"); const keyTypeEncoded = sshEncodeBuffer(keyTypeStr); const base64Encoded = pubKeyDer.toString("base64"); const openSshKey = `ssh-rsa ${Buffer.concat([keyTypeEncoded]).toString("base64")} pp-generated`; return { privateKey: privKeyPem, publicKeyOpenssh: `ssh-rsa ${base64Encoded} pp-generated` }; } async function generateAndImportKeyPair(ec2, keyName) { const { privateKey, publicKeyOpenssh } = generateSshKeyPair(); await ec2.send(new import_client_ec2.ImportKeyPairCommand({ KeyName: keyName, PublicKeyMaterial: Buffer.from(publicKeyOpenssh) })); return { privateKey }; } async function createPreviewSecurityGroup(ec2, groupName, port) { const describe = await ec2.send(new import_client_ec2.DescribeSecurityGroupsCommand({ Filters: [{ Name: "group-name", Values: [groupName] }] })); if (describe.SecurityGroups && describe.SecurityGroups.length > 0) { return describe.SecurityGroups[0].GroupId; } const res = await ec2.send(new import_client_ec2.CreateSecurityGroupCommand({ GroupName: groupName, Description: `PP Preview security group: ${groupName}` })); const groupId = res.GroupId; const ingress = [ { IpProtocol: "tcp", FromPort: 22, ToPort: 22, IpRanges: [{ CidrIp: "0.0.0.0/0" }] } ]; if (port !== 22) { ingress.push({ IpProtocol: "tcp", FromPort: port, ToPort: port, IpRanges: [{ CidrIp: "0.0.0.0/0" }] }); } await ec2.send(new import_client_ec2.AuthorizeSecurityGroupIngressCommand({ GroupId: groupId, IpPermissions: ingress })); return groupId; } const BOOTSTRAP_SCRIPT = `#!/bin/bash set -e apt-get update -y apt-get install -y curl git unzip build-essential curl -fsSL https://get.docker.com | sh systemctl enable docker systemctl start docker apt-get install -y docker-compose-plugin curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash export NVM_DIR="/root/.nvm" source "$NVM_DIR/nvm.sh" nvm install --lts nvm alias default lts/* `; async function launchInstance(opts) { const ami = UBUNTU_22_04_AMI[opts.region] ?? UBUNTU_22_04_AMI["us-east-1"]; const tagSpecs = Object.entries(opts.tags).map(([k, v]) => ({ Key: k, Value: v })); tagSpecs.push({ Key: "Name", Value: `pp-preview-${opts.tags["pp:previewId"]}` }); const res = await opts.ec2.send(new import_client_ec2.RunInstancesCommand({ ImageId: ami, InstanceType: opts.instanceType, MinCount: 1, MaxCount: 1, KeyName: opts.keyName, SecurityGroupIds: [opts.securityGroupId], UserData: Buffer.from(BOOTSTRAP_SCRIPT).toString("base64"), TagSpecifications: [ { ResourceType: "instance", Tags: tagSpecs } ] })); return res.Instances[0].InstanceId; } async function waitForInstanceRunning(ec2, instanceId, maxWaitMs = 3e5) { const start = Date.now(); while (Date.now() - start < maxWaitMs) { const res = await ec2.send(new import_client_ec2.DescribeInstancesCommand({ InstanceIds: [instanceId] })); const inst = res.Reservations?.[0]?.Instances?.[0]; if (inst?.State?.Name === "running" && inst.PublicIpAddress) { return inst.PublicIpAddress; } await sleep(5e3); } throw new Error(`Instance ${instanceId} did not reach running state within timeout`); } async function terminateInstance(ec2, instanceId) { await ec2.send(new import_client_ec2.TerminateInstancesCommand({ InstanceIds: [instanceId] })); } async function deleteKeyPairAws(ec2, keyName) { try { await ec2.send(new import_client_ec2.DeleteKeyPairCommand({ KeyName: keyName })); } catch (e) { log.warn({ e, keyName }, "Failed to delete key pair"); } } async function deleteSecurityGroupAws(ec2, groupName) { try { const describe = await ec2.send(new import_client_ec2.DescribeSecurityGroupsCommand({ Filters: [{ Name: "group-name", Values: [groupName] }] })); const groupId = describe.SecurityGroups?.[0]?.GroupId; if (groupId) { await sleep(5e3); await ec2.send(new import_client_ec2.DeleteSecurityGroupCommand({ GroupId: groupId })); } } catch (e) { log.warn({ e, groupName }, "Failed to delete security group"); } } async function describeAllManagedInstances(ec2) { const res = await ec2.send(new import_client_ec2.DescribeInstancesCommand({ Filters: [ { Name: "tag:pp:managed", Values: ["true"] }, { Name: "instance-state-name", Values: ["running", "pending", "stopping"] } ] })); return (res.Reservations ?? []).flatMap((r) => r.Instances ?? []); } function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { createPreviewSecurityGroup, deleteKeyPairAws, deleteSecurityGroupAws, describeAllManagedInstances, generateAndImportKeyPair, generateSshKeyPair, launchInstance, makeEc2Client, makeStsClient, terminateInstance, validateAwsCredentials, waitForInstanceRunning }); //# sourceMappingURL=ec2.js.map