@highstate/common 0.9.13 → 0.9.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-2JB6FMYR.js → chunk-NISDP46H.js} +20 -5
- package/dist/chunk-NISDP46H.js.map +1 -0
- package/dist/highstate.manifest.json +9 -9
- package/dist/index.js +1 -1
- package/dist/units/dns/record-set/index.js +1 -1
- package/dist/units/existing-server/index.js +1 -1
- package/dist/units/network/l3-endpoint/index.js +1 -1
- package/dist/units/network/l4-endpoint/index.js +1 -1
- package/dist/units/script/index.js +1 -1
- package/dist/units/server-dns/index.js +1 -1
- package/dist/units/server-patch/index.js +1 -1
- package/dist/units/ssh/key-pair/index.js +1 -1
- package/package.json +7 -6
- package/src/shared/ssh.ts +15 -5
- package/dist/chunk-2JB6FMYR.js.map +0 -1
@@ -394,13 +394,21 @@ function generatePassword() {
|
|
394
394
|
// src/shared/ssh.ts
|
395
395
|
import {
|
396
396
|
getOrCreateSecret,
|
397
|
-
getUnitInstanceName,
|
398
397
|
output as output3,
|
399
398
|
secret
|
400
399
|
} from "@highstate/pulumi";
|
401
400
|
import getKeys, { PrivateExport } from "micro-key-producer/ssh.js";
|
402
401
|
import { randomBytes as randomBytes2 } from "micro-key-producer/utils.js";
|
403
402
|
import { local as local2, remote as remote2 } from "@pulumi/command";
|
403
|
+
|
404
|
+
// assets/images.json
|
405
|
+
var terminal_ssh = {
|
406
|
+
name: "ghcr.io/exeteres/highstate/terminal-ssh",
|
407
|
+
tag: "latest",
|
408
|
+
image: "ghcr.io/exeteres/highstate/terminal-ssh:latest@sha256:99380e0405522afa0058eedce124c1970a87408663365b2dbce737801a7cd5d1"
|
409
|
+
};
|
410
|
+
|
411
|
+
// src/shared/ssh.ts
|
404
412
|
function createSshTerminal(credentials) {
|
405
413
|
return output3(credentials).apply((credentials2) => {
|
406
414
|
if (!credentials2) {
|
@@ -418,9 +426,10 @@ function createSshTerminal(credentials) {
|
|
418
426
|
}
|
419
427
|
return {
|
420
428
|
name: "ssh",
|
421
|
-
title:
|
429
|
+
title: "Shell",
|
422
430
|
description: "Connect to the server via SSH",
|
423
|
-
|
431
|
+
icon: "gg:remote",
|
432
|
+
image: terminal_ssh.image,
|
424
433
|
command,
|
425
434
|
files: {
|
426
435
|
"/password": credentials2.password,
|
@@ -460,7 +469,7 @@ function getOrCreateSshKeyPair(inputs, secrets) {
|
|
460
469
|
const privateKey = getOrCreateSecret(secrets, "sshPrivateKey", generatePrivateKey);
|
461
470
|
return privateKey.apply(privateKeyToKeyPair);
|
462
471
|
}
|
463
|
-
function createServerEntity(fallbackHostname, endpoint, sshPort = 22, sshUser = "root", sshPassword, sshPrivateKey) {
|
472
|
+
function createServerEntity(fallbackHostname, endpoint, sshPort = 22, sshUser = "root", sshPassword, sshPrivateKey, hasSsh = true) {
|
464
473
|
const connection = output3({
|
465
474
|
host: l3EndpointToString(endpoint),
|
466
475
|
port: sshPort,
|
@@ -469,6 +478,12 @@ function createServerEntity(fallbackHostname, endpoint, sshPort = 22, sshUser =
|
|
469
478
|
privateKey: sshPrivateKey,
|
470
479
|
dialErrorLimit: 3
|
471
480
|
});
|
481
|
+
if (!hasSsh) {
|
482
|
+
return output3({
|
483
|
+
hostname: fallbackHostname,
|
484
|
+
endpoints: [endpoint]
|
485
|
+
});
|
486
|
+
}
|
472
487
|
const command = new local2.Command("check-ssh", {
|
473
488
|
create: `nc -zv ${l3EndpointToString(endpoint)} ${sshPort} && echo "up" || echo "down"`
|
474
489
|
});
|
@@ -528,4 +543,4 @@ export {
|
|
528
543
|
getOrCreateSshKeyPair,
|
529
544
|
createServerEntity
|
530
545
|
};
|
531
|
-
//# sourceMappingURL=chunk-
|
546
|
+
//# sourceMappingURL=chunk-NISDP46H.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../src/shared/network.ts","../src/shared/command.ts","../src/shared/dns.ts","../src/shared/passwords.ts","../src/shared/ssh.ts","../assets/images.json"],"sourcesContent":["import type { ArrayPatchMode, network } from \"@highstate/library\"\nimport { toPromise, type Input } from \"@highstate/pulumi\"\nimport { uniqueBy } from \"remeda\"\n\n/**\n * The L3 or L4 endpoint for some service.\n *\n * The format is: `[protocol://]endpoint[:port]`\n */\nexport type InputL34Endpoint = network.L34Endpoint | string\n\n/**\n * The L3 endpoint for some service.\n */\nexport type InputL3Endpoint = network.L3Endpoint | string\n\n/**\n * The L4 endpoint for some service.\n */\nexport type InputL4Endpoint = network.L4Endpoint | string\n\n/**\n * Stringifies a L3 endpoint object into a string.\n *\n * @param l3Endpoint The L3 endpoint object to stringify.\n * @returns The string representation of the L3 endpoint.\n */\nexport function l3EndpointToString(l3Endpoint: network.L3Endpoint): string {\n switch (l3Endpoint.type) {\n case \"ipv4\":\n return l3Endpoint.address\n case \"ipv6\":\n return l3Endpoint.address\n case \"hostname\":\n return l3Endpoint.hostname\n }\n}\n\n/**\n * Stringifies a L4 endpoint object into a string.\n *\n * @param l4Endpoint The L4 endpoint object to stringify.\n *\n * @returns The string representation of the L4 endpoint.\n */\nexport function l4EndpointToString(l4Endpoint: network.L4Endpoint): string {\n if (l4Endpoint.type === \"ipv6\") {\n return `[${l4Endpoint.address}]:${l4Endpoint.port}`\n }\n\n return `${l3EndpointToString(l4Endpoint)}:${l4Endpoint.port}`\n}\n\n/**\n * Stringifies a L3 or L4 endpoint object into a string.\n *\n * @param l34Endpoint The L3 or L4 endpoint object to stringify.\n * @returns The string representation of the L3 or L4 endpoint.\n */\nexport function l34EndpointToString(l34Endpoint: network.L34Endpoint): string {\n if (l34Endpoint.port) {\n return l4EndpointToString(l34Endpoint)\n }\n\n return l3EndpointToString(l34Endpoint)\n}\n\nconst L34_ENDPOINT_RE =\n /^(?:(?<protocol>[a-z]+):\\/\\/)?(?:(?:\\[?(?<ipv6>[0-9A-Fa-f:]+)\\]?)|(?<ipv4>(?:\\d{1,3}\\.){3}\\d{1,3})|(?<hostname>[a-zA-Z0-9-*]+(?:\\.[a-zA-Z0-9-*]+)*))(?::(?<port>\\d{1,5}))?$/\n\n/**\n * Parses a L3 or L4 endpoint from a string.\n *\n * The format is `[protocol://]endpoint[:port]`.\n *\n * @param l34Endpoint The L3 or L4 endpoint string to parse.\n * @returns The parsed L3 or L4 endpoint object.\n */\nexport function parseL34Endpoint(l34Endpoint: InputL34Endpoint): network.L34Endpoint {\n if (typeof l34Endpoint === \"object\") {\n return l34Endpoint\n }\n\n const match = l34Endpoint.match(L34_ENDPOINT_RE)\n if (!match) {\n throw new Error(`Invalid L3/L4 endpoint: \"${l34Endpoint}\"`)\n }\n\n const { protocol, ipv6, ipv4, hostname, port } = match.groups!\n\n if (protocol && protocol !== \"tcp\" && protocol !== \"udp\") {\n throw new Error(`Invalid L4 endpoint protocol: \"${protocol}\"`)\n }\n\n let visibility: network.EndpointVisibility = \"public\"\n\n if (ipv4 && IPV4_PRIVATE_REGEX.test(ipv4)) {\n visibility = \"external\"\n } else if (ipv6 && IPV6_PRIVATE_REGEX.test(ipv6)) {\n visibility = \"external\"\n }\n\n const fallbackProtocol = port ? \"tcp\" : undefined\n\n return {\n type: ipv6 ? \"ipv6\" : ipv4 ? \"ipv4\" : \"hostname\",\n visibility,\n address: ipv6 || ipv4,\n hostname: hostname,\n port: port ? parseInt(port, 10) : undefined,\n protocol: protocol ? (protocol as network.L4Protocol) : fallbackProtocol,\n } as network.L34Endpoint\n}\n\n/**\n * Parses a L3 endpoint from a string.\n *\n * The same as `parseL34Endpoint`, but only for L3 endpoints and will throw an error if the endpoint contains a port.\n *\n * @param l3Endpoint The L3 endpoint string to parse.\n * @returns The parsed L3 endpoint object.\n */\nexport function parseL3Endpoint(l3Endpoint: InputL3Endpoint): network.L3Endpoint {\n if (typeof l3Endpoint === \"object\") {\n return l3Endpoint\n }\n\n const parsed = parseL34Endpoint(l3Endpoint)\n\n if (parsed.port) {\n throw new Error(`Port cannot be specified in L3 endpoint: \"${l3Endpoint}\"`)\n }\n\n return parsed\n}\n\n/**\n * Parses a L4 endpoint from a string.\n *\n * The same as `parseL34Endpoint`, but only for L4 endpoints and will throw an error if the endpoint does not contain a port.\n */\nexport function parseL4Endpoint(l4Endpoint: InputL4Endpoint): network.L4Endpoint {\n if (typeof l4Endpoint === \"object\") {\n return l4Endpoint\n }\n\n const parsed = parseL34Endpoint(l4Endpoint)\n\n if (!parsed.port) {\n throw new Error(`No port found in L4 endpoint: \"${l4Endpoint}\"`)\n }\n\n return parsed\n}\n\nconst IPV4_PRIVATE_REGEX =\n /^(?:10|127)(?:\\.\\d{1,3}){3}$|^(?:172\\.1[6-9]|172\\.2[0-9]|172\\.3[0-1])(?:\\.\\d{1,3}){2}$|^(?:192\\.168)(?:\\.\\d{1,3}){2}$/\n\nconst IPV6_PRIVATE_REGEX =\n /^(?:fc|fd)(?:[0-9a-f]{2}){0,2}::(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}$|^::(?:ffff:(?:10|127)(?:\\.\\d{1,3}){3}|(?:172\\.1[6-9]|172\\.2[0-9]|172\\.3[0-1])(?:\\.\\d{1,3}){2}|(?:192\\.168)(?:\\.\\d{1,3}){2})$/\n\n/**\n * Helper function to get the input L3 endpoint from the raw endpoint or input endpoint.\n *\n * If neither is provided, an error is thrown.\n *\n * @param rawEndpoint The raw endpoint string to parse.\n * @param inputEndpoint The input endpoint object to use if the raw endpoint is not provided.\n * @returns The parsed L3 endpoint object.\n */\nexport async function requireInputL3Endpoint(\n rawEndpoint: string | undefined,\n inputEndpoint: Input<network.L3Endpoint> | undefined,\n): Promise<network.L3Endpoint> {\n if (rawEndpoint) {\n return parseL3Endpoint(rawEndpoint)\n }\n\n if (inputEndpoint) {\n return toPromise(inputEndpoint)\n }\n\n throw new Error(\"No endpoint provided\")\n}\n\n/**\n * Helper function to get the input L4 endpoint from the raw endpoint or input endpoint.\n *\n * If neither is provided, an error is thrown.\n *\n * @param rawEndpoint The raw endpoint string to parse.\n * @param inputEndpoint The input endpoint object to use if the raw endpoint is not provided.\n * @returns The parsed L4 endpoint object.\n */\nexport async function requireInputL4Endpoint(\n rawEndpoint: string | undefined,\n inputEndpoint: Input<network.L4Endpoint> | undefined,\n): Promise<network.L4Endpoint> {\n if (rawEndpoint) {\n return parseL4Endpoint(rawEndpoint)\n }\n\n if (inputEndpoint) {\n return toPromise(inputEndpoint)\n }\n\n throw new Error(\"No endpoint provided\")\n}\n\n/**\n * Convers L3 endpoint to L4 endpoint by adding a port and protocol.\n *\n * @param l3Endpoint The L3 endpoint to convert.\n * @param port The port to add to the L3 endpoint.\n * @param protocol The protocol to add to the L3 endpoint. Defaults to \"tcp\".\n * @returns The L4 endpoint with the port and protocol added.\n */\nexport function l3ToL4Endpoint(\n l3Endpoint: InputL3Endpoint,\n port: number,\n protocol: network.L4Protocol = \"tcp\",\n): network.L4Endpoint {\n return {\n ...parseL3Endpoint(l3Endpoint),\n port,\n protocol,\n }\n}\n\n/**\n * Filters the endpoints based on the given filter.\n *\n * @param endpoints The list of endpoints to filter.\n * @param filter The filter to apply. If not provided, the endpoints will be filtered by the most accessible type: `public` > `external` > `internal`.\n * @param types The list of endpoint types to filter by. If provided, only endpoints of these types will be returned.\n *\n * @returns The filtered list of endpoints.\n */\nexport function filterEndpoints<TEndpoint extends network.L34Endpoint>(\n endpoints: TEndpoint[],\n filter?: network.EndpointFilter,\n types?: network.L34Endpoint[\"type\"][],\n): TEndpoint[] {\n if (filter?.length) {\n endpoints = endpoints.filter(endpoint => filter.includes(endpoint.visibility))\n } else if (endpoints.some(endpoint => endpoint.visibility === \"public\")) {\n endpoints = endpoints.filter(endpoint => endpoint.visibility === \"public\")\n } else if (endpoints.some(endpoint => endpoint.visibility === \"external\")) {\n endpoints = endpoints.filter(endpoint => endpoint.visibility === \"external\")\n }\n\n if (types && types.length) {\n endpoints = endpoints.filter(endpoint => types.includes(endpoint.type))\n }\n\n return endpoints\n}\n\n/**\n * Converts a L3 endpoint to CIDR notation.\n *\n * If the endpoint is a hostname, an error is thrown.\n *\n * @param l3Endpoint The L3 endpoint to convert.\n * @returns The CIDR notation of the L3 endpoint.\n */\nexport function l3EndpointToCidr(l3Endpoint: network.L3Endpoint): string {\n switch (l3Endpoint.type) {\n case \"ipv4\":\n return `${l3Endpoint.address}/32`\n case \"ipv6\":\n return `${l3Endpoint.address}/128`\n case \"hostname\":\n throw new Error(\"Cannot convert hostname to CIDR\")\n }\n}\n\n/**\n * Updates the endpoints based on the given mode.\n *\n * @param currentEndpoints The current endpoints to update.\n * @param endpoints The new endpoints to add in string format.\n * @param inputEndpoints The input endpoints to add in object format.\n * @param mode The mode to use when updating the endpoints. Can be \"replace\" or \"prepend\". Defaults to \"prepend\".\n *\n * @returns The updated list of endpoints.\n */\nexport async function updateEndpoints<TEdnpoints extends network.L34Endpoint>(\n currentEndpoints: Input<TEdnpoints[]>,\n endpoints: string[],\n inputEndpoints: Input<TEdnpoints[]>,\n mode: ArrayPatchMode = \"prepend\",\n): Promise<TEdnpoints[]> {\n const resolvedCurrentEndpoints = await toPromise(currentEndpoints)\n const resolvedInputEndpoints = await toPromise(inputEndpoints)\n\n const newEndpoints = uniqueBy(\n //\n [...endpoints.map(parseL34Endpoint), ...resolvedInputEndpoints],\n endpoint => l34EndpointToString(endpoint),\n )\n\n if (mode === \"replace\") {\n return newEndpoints as TEdnpoints[]\n }\n\n return uniqueBy(\n //\n [...newEndpoints, ...resolvedCurrentEndpoints],\n endpoint => l34EndpointToString(endpoint),\n ) as TEdnpoints[]\n}\n","import { local, remote, type types } from \"@pulumi/command\"\nimport {\n ComponentResource,\n interpolate,\n output,\n type ComponentResourceOptions,\n type Input,\n type InputOrArray,\n type Output,\n} from \"@highstate/pulumi\"\nimport { common, ssh } from \"@highstate/library\"\nimport { l3EndpointToString } from \"./network\"\n\nexport function getServerConnection(\n ssh: Input<ssh.Credentials>,\n): Output<types.input.remote.ConnectionArgs> {\n return output(ssh).apply(ssh => ({\n host: l3EndpointToString(ssh.endpoints[0]),\n port: ssh.endpoints[0].port,\n user: ssh.user,\n password: ssh.password,\n privateKey: ssh.keyPair?.privateKey,\n dialErrorLimit: 3,\n hostKey: ssh.hostKey,\n }))\n}\n\nexport type CommandHost = \"local\" | common.Server\n\nexport type CommandArgs = {\n host: Input<CommandHost>\n create: Input<string>\n update?: Input<string>\n delete?: Input<string>\n logging?: Input<remote.Logging>\n triggers?: InputOrArray<unknown>\n}\n\nexport type TextFileArgs = {\n host: CommandHost\n path: Input<string>\n content: Input<string>\n}\n\nexport class Command extends ComponentResource {\n public readonly command: Output<local.Command | remote.Command>\n\n public readonly stdout: Output<string>\n public readonly stderr: Output<string>\n\n constructor(name: string, args: CommandArgs, opts?: ComponentResourceOptions) {\n super(\"highstate:common:Command\", name, args, opts)\n\n this.command = output(args).apply(args => {\n if (args.host === \"local\") {\n return new local.Command(\n name,\n {\n create: args.create,\n update: args.update,\n delete: args.delete,\n logging: args.logging,\n triggers: args.triggers,\n },\n { ...opts, parent: this },\n )\n }\n\n if (!args.host.ssh) {\n throw new Error(`The host \"${args.host.hostname}\" has no SSH credentials`)\n }\n\n return new remote.Command(\n name,\n {\n connection: getServerConnection(args.host.ssh),\n create: args.create,\n update: args.update,\n delete: args.delete,\n logging: args.logging,\n triggers: args.triggers,\n },\n { ...opts, parent: this },\n )\n })\n\n this.stdout = this.command.stdout\n this.stderr = this.command.stderr\n }\n\n static createTextFile(\n name: string,\n options: TextFileArgs,\n opts?: ComponentResourceOptions,\n ): Output<Command> {\n return output(options).apply(options => {\n const escapedContent = options.content.replace(/\"/g, '\\\\\"')\n\n const command = new Command(\n name,\n {\n host: options.host,\n create: interpolate`mkdir -p $(dirname ${options.path}) && echo \"${escapedContent}\" > ${options.path}`,\n delete: interpolate`rm -rf ${options.path}`,\n },\n opts,\n )\n\n return command\n })\n }\n\n static receiveTextFile(\n name: string,\n options: Omit<TextFileArgs, \"content\">,\n opts?: ComponentResourceOptions,\n ): Output<Command> {\n return output(options).apply(options => {\n const command = new Command(\n name,\n {\n host: options.host,\n create: interpolate`while ! test -f ${options.path}; do sleep 1; done; cat ${options.path}`,\n logging: \"stderr\",\n },\n opts,\n )\n\n return command\n })\n }\n}\n","import type { ArrayPatchMode, dns, network } from \"@highstate/library\"\nimport {\n ComponentResource,\n normalize,\n output,\n Output,\n Resource,\n toPromise,\n type Input,\n type InputArray,\n type InputOrArray,\n type ResourceOptions,\n type Unwrap,\n} from \"@highstate/pulumi\"\nimport { capitalize, groupBy, uniqueBy } from \"remeda\"\nimport { Command, type CommandHost } from \"./command\"\nimport {\n filterEndpoints,\n l34EndpointToString,\n l3EndpointToString,\n parseL3Endpoint,\n type InputL3Endpoint,\n} from \"./network\"\n\nexport type DnsRecordArgs = {\n /**\n * The DNS provider to use.\n */\n provider: Input<dns.Provider>\n\n /**\n * The name of the DNS record.\n * If not provided, the name of the resource will be used.\n */\n name?: Input<string>\n\n /**\n * The type of the DNS record.\n *\n * If not provided, will be automatically detected based on the value.\n */\n type?: Input<string>\n\n /**\n * The value of the DNS record.\n */\n value: Input<InputL3Endpoint>\n\n /**\n * Whether the DNS record is proxied (e.g. to provide DDoS protection).\n *\n * Available only for public IPs and some DNS providers like Cloudflare.\n * If not supported, the DNS provider will ignore this value.\n */\n proxied?: Input<boolean>\n\n /**\n * The TTL of the DNS record.\n *\n * If not provided, the DNS provider will use its default value.\n */\n ttl?: Input<number>\n\n /**\n * The priority of the DNS record.\n *\n * Only used for some DNS record types (e.g. MX).\n */\n priority?: Input<number>\n\n /**\n * Wait for the DNS record to be created/updated at the specified environment(s) before continuing.\n */\n waitAt?: InputOrArray<CommandHost>\n}\n\nexport type ResolvedDnsRecordArgs = Unwrap<Omit<DnsRecordArgs, \"value\" | \"type\">> & {\n /**\n * The value of the DNS record.\n */\n value: string\n\n /**\n * The type of the DNS record.\n */\n type: string\n}\n\nexport type DnsRecordSetArgs = Omit<DnsRecordArgs, \"provider\" | \"value\"> & {\n /**\n * The DNS providers to use to create the DNS records.\n *\n * If multiple providers matched the specified domain, multiple DNS records will be created.\n */\n providers: Input<dns.Provider[]>\n\n /**\n * The value of the DNS record.\n */\n value?: Input<InputL3Endpoint>\n\n /**\n * The values of the DNS records.\n */\n values?: InputArray<InputL3Endpoint>\n}\n\nfunction getTypeByEndpoint(endpoint: network.L3Endpoint): string {\n switch (endpoint.type) {\n case \"ipv4\":\n return \"A\"\n case \"ipv6\":\n return \"AAAA\"\n case \"hostname\":\n return \"CNAME\"\n }\n}\n\nexport abstract class DnsRecord extends ComponentResource {\n /**\n * The underlying dns record resource.\n */\n public readonly dnsRecord: Output<Resource>\n\n /**\n * The wait commands to be executed after the DNS record is created/updated.\n *\n * Use this field as a dependency for other resources.\n */\n public readonly waitCommands: Output<Command[]>\n\n protected constructor(name: string, args: DnsRecordArgs, opts?: ResourceOptions) {\n super(\"highstate:common:DnsRecord\", name, args, opts)\n\n this.dnsRecord = output(args).apply(args => {\n const l3Endpoint = parseL3Endpoint(args.value)\n const type = args.type ?? getTypeByEndpoint(l3Endpoint)\n\n return output(\n this.create(\n name,\n {\n ...args,\n type,\n value: l3EndpointToString(l3Endpoint),\n },\n { ...opts, parent: this },\n ),\n )\n })\n\n this.waitCommands = output(args).apply(args => {\n const waitAt = args.waitAt ? (Array.isArray(args.waitAt) ? args.waitAt : [args.waitAt]) : []\n\n return waitAt.map(host => {\n const hostname = host === \"local\" ? \"local\" : host.hostname\n\n return new Command(\n `${name}-wait-${hostname}`,\n {\n host,\n create: `while ! getent hosts ${args.name} >/dev/null; do echo \"Waiting for DNS record ${args.name} to be created\"; sleep 5; done`,\n triggers: [args.type, args.ttl, args.priority, args.proxied],\n },\n { parent: this },\n )\n })\n })\n }\n\n protected abstract create(\n name: string,\n args: ResolvedDnsRecordArgs,\n opts?: ResourceOptions,\n ): Input<Resource>\n\n static create(name: string, args: DnsRecordArgs, opts?: ResourceOptions): Output<DnsRecord> {\n return output(args).apply(async args => {\n const providerType = args.provider.type\n const implName = `${capitalize(providerType)}DnsRecord`\n const implModule = (await import(`@highstate/${providerType}`)) as Record<string, unknown>\n\n const implClass = implModule[implName] as new (\n name: string,\n args: Unwrap<DnsRecordArgs>,\n opts?: ResourceOptions,\n ) => DnsRecord\n\n return new implClass(name, args, opts)\n })\n }\n}\n\nexport class DnsRecordSet extends ComponentResource {\n /**\n * The underlying dns record resources.\n */\n public readonly dnsRecords: Output<DnsRecord[]>\n\n /**\n * The wait commands to be executed after the DNS records are created/updated.\n */\n public readonly waitCommands: Output<Command[]>\n\n private constructor(name: string, records: Output<DnsRecord[]>, opts?: ResourceOptions) {\n super(\"highstate:common:DnsRecordSet\", name, records, opts)\n\n this.dnsRecords = records\n\n this.waitCommands = records.apply(records =>\n records.flatMap(record => record.waitCommands),\n ) as unknown as Output<Command[]>\n }\n\n static create(name: string, args: DnsRecordSetArgs, opts?: ResourceOptions): DnsRecordSet {\n const records = output(args).apply(args => {\n const recordName = args.name ?? name\n const values = normalize(args.value, args.values)\n\n return output(\n args.providers\n .filter(provider => recordName.endsWith(provider.domain))\n .flatMap(provider => {\n return values.map(value => {\n const l3Endpoint = parseL3Endpoint(value)\n\n return DnsRecord.create(\n `${provider.type}-from-${recordName}-to-${l3EndpointToString(l3Endpoint)}`,\n { name: recordName, ...args, value: l3Endpoint, provider },\n opts,\n )\n })\n }),\n )\n })\n\n return new DnsRecordSet(name, records, opts)\n }\n}\n\n/**\n * Registers the DNS record set for the given endpoints and prepends the corresponding hostname endpoint to the list.\n *\n * Waits for the DNS record set to be created/updated before continuing.\n *\n * Ignores the \"hostname\" endpoints in the list.\n *\n * @param endpoints The list of endpoints to register. Will be modified in place.\n * @param fqdn The FQDN to register the DNS record set for. If not provided, no DNS record set will be created and array will not be modified.\n * @param fqdnEndpointFilter The filter to apply to the endpoints before passing them to the DNS record set. Does not apply to the resulted endpoint list.\n * @param dnsProviders The DNS providers to use to create the DNS records.\n */\nexport async function updateEndpointsWithFqdn<TEndpoint extends network.L34Endpoint>(\n endpoints: Input<TEndpoint[]>,\n fqdn: string | undefined,\n fqdnEndpointFilter: network.EndpointFilter,\n patchMode: ArrayPatchMode,\n dnsProviders: Input<dns.Provider[]>,\n): Promise<{ endpoints: TEndpoint[]; dnsRecordSet: DnsRecordSet | undefined }> {\n const resolvedEndpoints = await toPromise(endpoints)\n\n if (!fqdn) {\n return {\n endpoints: resolvedEndpoints as TEndpoint[],\n dnsRecordSet: undefined,\n }\n }\n\n const filteredEndpoints = filterEndpoints(resolvedEndpoints, fqdnEndpointFilter)\n\n const dnsRecordSet = DnsRecordSet.create(fqdn, {\n providers: dnsProviders,\n values: filteredEndpoints,\n waitAt: \"local\",\n })\n\n const portProtocolGroups = groupBy(filteredEndpoints, endpoint =>\n endpoint.port ? `${endpoint.port}-${endpoint.protocol}` : \"\",\n )\n\n const newEndpoints: TEndpoint[] = []\n\n for (const group of Object.values(portProtocolGroups)) {\n newEndpoints.unshift({\n type: \"hostname\",\n hostname: fqdn,\n visibility: group[0].visibility,\n port: group[0].port,\n protocol: group[0].protocol,\n } as TEndpoint)\n }\n\n await toPromise(\n dnsRecordSet.waitCommands.apply(waitCommands => waitCommands.map(command => command.stdout)),\n )\n\n if (patchMode === \"prepend\") {\n return {\n endpoints: uniqueBy(\n //\n [...newEndpoints, ...(resolvedEndpoints as TEndpoint[])],\n endpoint => l34EndpointToString(endpoint),\n ),\n dnsRecordSet,\n }\n }\n\n return {\n endpoints: newEndpoints,\n dnsRecordSet,\n }\n}\n","import { randomBytes } from \"@noble/hashes/utils\"\nimport { secureMask } from \"micro-key-producer/password.js\"\n\nexport function generatePassword() {\n return secureMask.apply(randomBytes(32)).password\n}\n","import type { common, network, ssh } from \"@highstate/library\"\nimport {\n getOrCreateSecret,\n output,\n Output,\n secret,\n type Input,\n type InstanceTerminal,\n} from \"@highstate/pulumi\"\nimport getKeys, { PrivateExport } from \"micro-key-producer/ssh.js\"\nimport { randomBytes } from \"micro-key-producer/utils.js\"\nimport { local, remote } from \"@pulumi/command\"\nimport * as images from \"../../assets/images.json\"\nimport { l3EndpointToString, l3ToL4Endpoint } from \"./network\"\n\nexport function createSshTerminal(\n credentials: Input<ssh.Credentials | undefined>,\n): Output<InstanceTerminal | undefined> {\n return output(credentials).apply(credentials => {\n if (!credentials) {\n return undefined\n }\n\n const command = [\"ssh\", \"-tt\", \"-o\", \"UserKnownHostsFile=/known_hosts\"]\n\n // TODO: select best endpoint based on the environment\n const endpoint = credentials.endpoints[0]\n\n command.push(\"-p\", endpoint.port.toString())\n\n if (credentials.keyPair) {\n command.push(\"-i\", \"/private_key\")\n }\n\n command.push(`${credentials.user}@${l3EndpointToString(endpoint)}`)\n\n if (credentials.password) {\n command.unshift(\"sshpass\", \"-f\", \"/password\")\n }\n\n return {\n name: \"ssh\",\n title: \"Shell\",\n description: \"Connect to the server via SSH\",\n icon: \"gg:remote\",\n\n image: images[\"terminal-ssh\"].image,\n command,\n\n files: {\n \"/password\": credentials.password,\n\n \"/private_key\": {\n content: credentials.keyPair?.privateKey,\n mode: 0o600,\n },\n\n \"/known_hosts\": {\n content: `${l3EndpointToString(endpoint)} ${credentials.hostKey}`,\n mode: 0o644,\n },\n },\n } satisfies InstanceTerminal\n })\n}\n\nexport function generatePrivateKey(): string {\n const seed = randomBytes(32)\n\n return getKeys(seed).privateKey\n}\n\nexport function privateKeyToKeyPair(privateKeyString: Input<string>): Output<ssh.KeyPair> {\n return output(privateKeyString).apply(privateKeyString => {\n const privateKeyStruct = PrivateExport.decode(privateKeyString)\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const privKey = privateKeyStruct.keys[0].privKey.privKey as Uint8Array\n\n const { fingerprint, publicKey, privateKey } = getKeys(privKey.slice(0, 32))\n\n return output({\n type: \"ed25519\" as const,\n fingerprint,\n publicKey,\n privateKey: secret(privateKey),\n })\n })\n}\n\nexport type SshKeyPairInputs = {\n sshKeyPair?: Input<ssh.KeyPair>\n}\n\nexport type SshKeyPairSecrets = {\n sshPrivateKey?: Input<string>\n}\n\nexport function getOrCreateSshKeyPair(\n inputs: SshKeyPairInputs,\n secrets: Output<SshKeyPairSecrets>,\n): Output<ssh.KeyPair> {\n if (inputs.sshKeyPair) {\n return output(inputs.sshKeyPair)\n }\n\n const privateKey = getOrCreateSecret(secrets, \"sshPrivateKey\", generatePrivateKey)\n\n return privateKey.apply(privateKeyToKeyPair)\n}\n\nexport function createServerEntity(\n fallbackHostname: string,\n endpoint: network.L3Endpoint,\n sshPort = 22,\n sshUser = \"root\",\n sshPassword?: Input<string | undefined>,\n sshPrivateKey?: Input<string>,\n hasSsh = true,\n): Output<common.Server> {\n const connection = output({\n host: l3EndpointToString(endpoint),\n port: sshPort,\n user: sshUser,\n password: sshPassword,\n privateKey: sshPrivateKey,\n dialErrorLimit: 3,\n })\n\n if (!hasSsh) {\n return output({\n hostname: fallbackHostname,\n endpoints: [endpoint],\n })\n }\n\n const command = new local.Command(\"check-ssh\", {\n create: `nc -zv ${l3EndpointToString(endpoint)} ${sshPort} && echo \"up\" || echo \"down\"`,\n })\n\n return command.stdout.apply(result => {\n if (result === \"down\") {\n return output({\n hostname: fallbackHostname,\n endpoints: [endpoint],\n })\n }\n\n const hostnameResult = new remote.Command(\"hostname\", {\n connection,\n create: \"hostname\",\n triggers: [Date.now()],\n })\n\n const hostKeyResult = new remote.Command(\"host-key\", {\n connection,\n create: \"cat /etc/ssh/ssh_host_ed25519_key.pub\",\n triggers: [Date.now()],\n })\n\n return output({\n endpoints: [endpoint],\n hostname: hostnameResult.stdout.apply(x => x.trim()),\n ssh: {\n endpoints: [l3ToL4Endpoint(endpoint, sshPort)],\n user: sshUser,\n hostKey: hostKeyResult.stdout.apply(x => x.trim()),\n password: sshPassword,\n keyPair: sshPrivateKey ? privateKeyToKeyPair(sshPrivateKey) : undefined,\n },\n })\n })\n}\n","{\n \"terminal-ssh\": {\n \"name\": \"ghcr.io/exeteres/highstate/terminal-ssh\",\n \"tag\": \"latest\",\n \"image\": \"ghcr.io/exeteres/highstate/terminal-ssh:latest@sha256:99380e0405522afa0058eedce124c1970a87408663365b2dbce737801a7cd5d1\"\n }\n}\n"],"mappings":"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","names":["ssh","args","options","ComponentResource","output","toPromise","uniqueBy","ComponentResource","output","args","records","toPromise","uniqueBy","output","randomBytes","local","remote","terminal-ssh","output","credentials","terminal-ssh","randomBytes","privateKeyString","local","remote"]}
|
@@ -1,13 +1,13 @@
|
|
1
1
|
{
|
2
2
|
"sourceHashes": {
|
3
|
-
"./dist/index.js": "
|
4
|
-
"./dist/units/dns/record-set/index.js": "
|
5
|
-
"./dist/units/network/l3-endpoint/index.js": "
|
6
|
-
"./dist/units/network/l4-endpoint/index.js": "
|
7
|
-
"./dist/units/existing-server/index.js": "
|
8
|
-
"./dist/units/ssh/key-pair/index.js": "
|
9
|
-
"./dist/units/script/index.js": "
|
10
|
-
"./dist/units/server-dns/index.js": "
|
11
|
-
"./dist/units/server-patch/index.js": "
|
3
|
+
"./dist/index.js": "d974fe1ecb44ee6f8051c39ed4b6023c1e708020ab039ff86d7e4d03fc7b02dc",
|
4
|
+
"./dist/units/dns/record-set/index.js": "5e4174673a4de3e44d809a84adae05a3afe665fe483ed158e9e88f53153ae7ba",
|
5
|
+
"./dist/units/network/l3-endpoint/index.js": "9f5c2fdbbe1f2801c6bc290f061bb4127bc1171ee7bdf9f6f139f98793071134",
|
6
|
+
"./dist/units/network/l4-endpoint/index.js": "74c38a74e2ebf381071dc1cbf991cd331a0ee82005cddfa7e6766c5e3f4ec6b1",
|
7
|
+
"./dist/units/existing-server/index.js": "4cd262d67b62631cf937c9b975e49ba313fd41cc05c675c76baf835dbd35f58d",
|
8
|
+
"./dist/units/ssh/key-pair/index.js": "4c8143ff0f6010bcd9ca3aacbfd0ed45922042c179cf8408975e450b4620a08f",
|
9
|
+
"./dist/units/script/index.js": "4c46ee686a39ed388966065a4a6010f6103eedd18466e6a5413c2af89c44f9e7",
|
10
|
+
"./dist/units/server-dns/index.js": "f2bdaa8acfdfdd32c5ddb12e3efd610bb883b1f8fd4426ce661892f2d995ac0e",
|
11
|
+
"./dist/units/server-patch/index.js": "d26497c781ee08c7eafab81be3712e3c3577d48732ed6727467c245d0d7b8d17"
|
12
12
|
}
|
13
13
|
}
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@highstate/common",
|
3
|
-
"version": "0.9.
|
3
|
+
"version": "0.9.15",
|
4
4
|
"type": "module",
|
5
5
|
"files": [
|
6
6
|
"dist",
|
@@ -24,18 +24,19 @@
|
|
24
24
|
"access": "public"
|
25
25
|
},
|
26
26
|
"scripts": {
|
27
|
-
"build": "highstate build"
|
27
|
+
"build": "highstate build",
|
28
|
+
"update-images": "../../scripts/update-images.sh ./assets/images.json"
|
28
29
|
},
|
29
30
|
"dependencies": {
|
30
|
-
"@highstate/library": "^0.9.
|
31
|
-
"@highstate/pulumi": "^0.9.
|
31
|
+
"@highstate/library": "^0.9.15",
|
32
|
+
"@highstate/pulumi": "^0.9.15",
|
32
33
|
"@noble/hashes": "^1.7.1",
|
33
34
|
"@pulumi/command": "^1.0.2",
|
34
35
|
"micro-key-producer": "^0.7.3",
|
35
36
|
"remeda": "^2.21.0"
|
36
37
|
},
|
37
38
|
"devDependencies": {
|
38
|
-
"@highstate/cli": "^0.9.
|
39
|
+
"@highstate/cli": "^0.9.15"
|
39
40
|
},
|
40
|
-
"gitHead": "
|
41
|
+
"gitHead": "f61b9905d4cd50511b03331411f42595403ebc06"
|
41
42
|
}
|
package/src/shared/ssh.ts
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import type { common, network, ssh } from "@highstate/library"
|
2
2
|
import {
|
3
3
|
getOrCreateSecret,
|
4
|
-
getUnitInstanceName,
|
5
4
|
output,
|
6
5
|
Output,
|
7
6
|
secret,
|
@@ -11,7 +10,8 @@ import {
|
|
11
10
|
import getKeys, { PrivateExport } from "micro-key-producer/ssh.js"
|
12
11
|
import { randomBytes } from "micro-key-producer/utils.js"
|
13
12
|
import { local, remote } from "@pulumi/command"
|
14
|
-
import
|
13
|
+
import * as images from "../../assets/images.json"
|
14
|
+
import { l3EndpointToString, l3ToL4Endpoint } from "./network"
|
15
15
|
|
16
16
|
export function createSshTerminal(
|
17
17
|
credentials: Input<ssh.Credentials | undefined>,
|
@@ -40,9 +40,11 @@ export function createSshTerminal(
|
|
40
40
|
|
41
41
|
return {
|
42
42
|
name: "ssh",
|
43
|
-
title:
|
43
|
+
title: "Shell",
|
44
44
|
description: "Connect to the server via SSH",
|
45
|
-
|
45
|
+
icon: "gg:remote",
|
46
|
+
|
47
|
+
image: images["terminal-ssh"].image,
|
46
48
|
command,
|
47
49
|
|
48
50
|
files: {
|
@@ -58,7 +60,7 @@ export function createSshTerminal(
|
|
58
60
|
mode: 0o644,
|
59
61
|
},
|
60
62
|
},
|
61
|
-
}
|
63
|
+
} satisfies InstanceTerminal
|
62
64
|
})
|
63
65
|
}
|
64
66
|
|
@@ -114,6 +116,7 @@ export function createServerEntity(
|
|
114
116
|
sshUser = "root",
|
115
117
|
sshPassword?: Input<string | undefined>,
|
116
118
|
sshPrivateKey?: Input<string>,
|
119
|
+
hasSsh = true,
|
117
120
|
): Output<common.Server> {
|
118
121
|
const connection = output({
|
119
122
|
host: l3EndpointToString(endpoint),
|
@@ -124,6 +127,13 @@ export function createServerEntity(
|
|
124
127
|
dialErrorLimit: 3,
|
125
128
|
})
|
126
129
|
|
130
|
+
if (!hasSsh) {
|
131
|
+
return output({
|
132
|
+
hostname: fallbackHostname,
|
133
|
+
endpoints: [endpoint],
|
134
|
+
})
|
135
|
+
}
|
136
|
+
|
127
137
|
const command = new local.Command("check-ssh", {
|
128
138
|
create: `nc -zv ${l3EndpointToString(endpoint)} ${sshPort} && echo "up" || echo "down"`,
|
129
139
|
})
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"sources":["../src/shared/network.ts","../src/shared/command.ts","../src/shared/dns.ts","../src/shared/passwords.ts","../src/shared/ssh.ts"],"sourcesContent":["import type { ArrayPatchMode, network } from \"@highstate/library\"\nimport { toPromise, type Input } from \"@highstate/pulumi\"\nimport { uniqueBy } from \"remeda\"\n\n/**\n * The L3 or L4 endpoint for some service.\n *\n * The format is: `[protocol://]endpoint[:port]`\n */\nexport type InputL34Endpoint = network.L34Endpoint | string\n\n/**\n * The L3 endpoint for some service.\n */\nexport type InputL3Endpoint = network.L3Endpoint | string\n\n/**\n * The L4 endpoint for some service.\n */\nexport type InputL4Endpoint = network.L4Endpoint | string\n\n/**\n * Stringifies a L3 endpoint object into a string.\n *\n * @param l3Endpoint The L3 endpoint object to stringify.\n * @returns The string representation of the L3 endpoint.\n */\nexport function l3EndpointToString(l3Endpoint: network.L3Endpoint): string {\n switch (l3Endpoint.type) {\n case \"ipv4\":\n return l3Endpoint.address\n case \"ipv6\":\n return l3Endpoint.address\n case \"hostname\":\n return l3Endpoint.hostname\n }\n}\n\n/**\n * Stringifies a L4 endpoint object into a string.\n *\n * @param l4Endpoint The L4 endpoint object to stringify.\n *\n * @returns The string representation of the L4 endpoint.\n */\nexport function l4EndpointToString(l4Endpoint: network.L4Endpoint): string {\n if (l4Endpoint.type === \"ipv6\") {\n return `[${l4Endpoint.address}]:${l4Endpoint.port}`\n }\n\n return `${l3EndpointToString(l4Endpoint)}:${l4Endpoint.port}`\n}\n\n/**\n * Stringifies a L3 or L4 endpoint object into a string.\n *\n * @param l34Endpoint The L3 or L4 endpoint object to stringify.\n * @returns The string representation of the L3 or L4 endpoint.\n */\nexport function l34EndpointToString(l34Endpoint: network.L34Endpoint): string {\n if (l34Endpoint.port) {\n return l4EndpointToString(l34Endpoint)\n }\n\n return l3EndpointToString(l34Endpoint)\n}\n\nconst L34_ENDPOINT_RE =\n /^(?:(?<protocol>[a-z]+):\\/\\/)?(?:(?:\\[?(?<ipv6>[0-9A-Fa-f:]+)\\]?)|(?<ipv4>(?:\\d{1,3}\\.){3}\\d{1,3})|(?<hostname>[a-zA-Z0-9-*]+(?:\\.[a-zA-Z0-9-*]+)*))(?::(?<port>\\d{1,5}))?$/\n\n/**\n * Parses a L3 or L4 endpoint from a string.\n *\n * The format is `[protocol://]endpoint[:port]`.\n *\n * @param l34Endpoint The L3 or L4 endpoint string to parse.\n * @returns The parsed L3 or L4 endpoint object.\n */\nexport function parseL34Endpoint(l34Endpoint: InputL34Endpoint): network.L34Endpoint {\n if (typeof l34Endpoint === \"object\") {\n return l34Endpoint\n }\n\n const match = l34Endpoint.match(L34_ENDPOINT_RE)\n if (!match) {\n throw new Error(`Invalid L3/L4 endpoint: \"${l34Endpoint}\"`)\n }\n\n const { protocol, ipv6, ipv4, hostname, port } = match.groups!\n\n if (protocol && protocol !== \"tcp\" && protocol !== \"udp\") {\n throw new Error(`Invalid L4 endpoint protocol: \"${protocol}\"`)\n }\n\n let visibility: network.EndpointVisibility = \"public\"\n\n if (ipv4 && IPV4_PRIVATE_REGEX.test(ipv4)) {\n visibility = \"external\"\n } else if (ipv6 && IPV6_PRIVATE_REGEX.test(ipv6)) {\n visibility = \"external\"\n }\n\n const fallbackProtocol = port ? \"tcp\" : undefined\n\n return {\n type: ipv6 ? \"ipv6\" : ipv4 ? \"ipv4\" : \"hostname\",\n visibility,\n address: ipv6 || ipv4,\n hostname: hostname,\n port: port ? parseInt(port, 10) : undefined,\n protocol: protocol ? (protocol as network.L4Protocol) : fallbackProtocol,\n } as network.L34Endpoint\n}\n\n/**\n * Parses a L3 endpoint from a string.\n *\n * The same as `parseL34Endpoint`, but only for L3 endpoints and will throw an error if the endpoint contains a port.\n *\n * @param l3Endpoint The L3 endpoint string to parse.\n * @returns The parsed L3 endpoint object.\n */\nexport function parseL3Endpoint(l3Endpoint: InputL3Endpoint): network.L3Endpoint {\n if (typeof l3Endpoint === \"object\") {\n return l3Endpoint\n }\n\n const parsed = parseL34Endpoint(l3Endpoint)\n\n if (parsed.port) {\n throw new Error(`Port cannot be specified in L3 endpoint: \"${l3Endpoint}\"`)\n }\n\n return parsed\n}\n\n/**\n * Parses a L4 endpoint from a string.\n *\n * The same as `parseL34Endpoint`, but only for L4 endpoints and will throw an error if the endpoint does not contain a port.\n */\nexport function parseL4Endpoint(l4Endpoint: InputL4Endpoint): network.L4Endpoint {\n if (typeof l4Endpoint === \"object\") {\n return l4Endpoint\n }\n\n const parsed = parseL34Endpoint(l4Endpoint)\n\n if (!parsed.port) {\n throw new Error(`No port found in L4 endpoint: \"${l4Endpoint}\"`)\n }\n\n return parsed\n}\n\nconst IPV4_PRIVATE_REGEX =\n /^(?:10|127)(?:\\.\\d{1,3}){3}$|^(?:172\\.1[6-9]|172\\.2[0-9]|172\\.3[0-1])(?:\\.\\d{1,3}){2}$|^(?:192\\.168)(?:\\.\\d{1,3}){2}$/\n\nconst IPV6_PRIVATE_REGEX =\n /^(?:fc|fd)(?:[0-9a-f]{2}){0,2}::(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}$|^::(?:ffff:(?:10|127)(?:\\.\\d{1,3}){3}|(?:172\\.1[6-9]|172\\.2[0-9]|172\\.3[0-1])(?:\\.\\d{1,3}){2}|(?:192\\.168)(?:\\.\\d{1,3}){2})$/\n\n/**\n * Helper function to get the input L3 endpoint from the raw endpoint or input endpoint.\n *\n * If neither is provided, an error is thrown.\n *\n * @param rawEndpoint The raw endpoint string to parse.\n * @param inputEndpoint The input endpoint object to use if the raw endpoint is not provided.\n * @returns The parsed L3 endpoint object.\n */\nexport async function requireInputL3Endpoint(\n rawEndpoint: string | undefined,\n inputEndpoint: Input<network.L3Endpoint> | undefined,\n): Promise<network.L3Endpoint> {\n if (rawEndpoint) {\n return parseL3Endpoint(rawEndpoint)\n }\n\n if (inputEndpoint) {\n return toPromise(inputEndpoint)\n }\n\n throw new Error(\"No endpoint provided\")\n}\n\n/**\n * Helper function to get the input L4 endpoint from the raw endpoint or input endpoint.\n *\n * If neither is provided, an error is thrown.\n *\n * @param rawEndpoint The raw endpoint string to parse.\n * @param inputEndpoint The input endpoint object to use if the raw endpoint is not provided.\n * @returns The parsed L4 endpoint object.\n */\nexport async function requireInputL4Endpoint(\n rawEndpoint: string | undefined,\n inputEndpoint: Input<network.L4Endpoint> | undefined,\n): Promise<network.L4Endpoint> {\n if (rawEndpoint) {\n return parseL4Endpoint(rawEndpoint)\n }\n\n if (inputEndpoint) {\n return toPromise(inputEndpoint)\n }\n\n throw new Error(\"No endpoint provided\")\n}\n\n/**\n * Convers L3 endpoint to L4 endpoint by adding a port and protocol.\n *\n * @param l3Endpoint The L3 endpoint to convert.\n * @param port The port to add to the L3 endpoint.\n * @param protocol The protocol to add to the L3 endpoint. Defaults to \"tcp\".\n * @returns The L4 endpoint with the port and protocol added.\n */\nexport function l3ToL4Endpoint(\n l3Endpoint: InputL3Endpoint,\n port: number,\n protocol: network.L4Protocol = \"tcp\",\n): network.L4Endpoint {\n return {\n ...parseL3Endpoint(l3Endpoint),\n port,\n protocol,\n }\n}\n\n/**\n * Filters the endpoints based on the given filter.\n *\n * @param endpoints The list of endpoints to filter.\n * @param filter The filter to apply. If not provided, the endpoints will be filtered by the most accessible type: `public` > `external` > `internal`.\n * @param types The list of endpoint types to filter by. If provided, only endpoints of these types will be returned.\n *\n * @returns The filtered list of endpoints.\n */\nexport function filterEndpoints<TEndpoint extends network.L34Endpoint>(\n endpoints: TEndpoint[],\n filter?: network.EndpointFilter,\n types?: network.L34Endpoint[\"type\"][],\n): TEndpoint[] {\n if (filter?.length) {\n endpoints = endpoints.filter(endpoint => filter.includes(endpoint.visibility))\n } else if (endpoints.some(endpoint => endpoint.visibility === \"public\")) {\n endpoints = endpoints.filter(endpoint => endpoint.visibility === \"public\")\n } else if (endpoints.some(endpoint => endpoint.visibility === \"external\")) {\n endpoints = endpoints.filter(endpoint => endpoint.visibility === \"external\")\n }\n\n if (types && types.length) {\n endpoints = endpoints.filter(endpoint => types.includes(endpoint.type))\n }\n\n return endpoints\n}\n\n/**\n * Converts a L3 endpoint to CIDR notation.\n *\n * If the endpoint is a hostname, an error is thrown.\n *\n * @param l3Endpoint The L3 endpoint to convert.\n * @returns The CIDR notation of the L3 endpoint.\n */\nexport function l3EndpointToCidr(l3Endpoint: network.L3Endpoint): string {\n switch (l3Endpoint.type) {\n case \"ipv4\":\n return `${l3Endpoint.address}/32`\n case \"ipv6\":\n return `${l3Endpoint.address}/128`\n case \"hostname\":\n throw new Error(\"Cannot convert hostname to CIDR\")\n }\n}\n\n/**\n * Updates the endpoints based on the given mode.\n *\n * @param currentEndpoints The current endpoints to update.\n * @param endpoints The new endpoints to add in string format.\n * @param inputEndpoints The input endpoints to add in object format.\n * @param mode The mode to use when updating the endpoints. Can be \"replace\" or \"prepend\". Defaults to \"prepend\".\n *\n * @returns The updated list of endpoints.\n */\nexport async function updateEndpoints<TEdnpoints extends network.L34Endpoint>(\n currentEndpoints: Input<TEdnpoints[]>,\n endpoints: string[],\n inputEndpoints: Input<TEdnpoints[]>,\n mode: ArrayPatchMode = \"prepend\",\n): Promise<TEdnpoints[]> {\n const resolvedCurrentEndpoints = await toPromise(currentEndpoints)\n const resolvedInputEndpoints = await toPromise(inputEndpoints)\n\n const newEndpoints = uniqueBy(\n //\n [...endpoints.map(parseL34Endpoint), ...resolvedInputEndpoints],\n endpoint => l34EndpointToString(endpoint),\n )\n\n if (mode === \"replace\") {\n return newEndpoints as TEdnpoints[]\n }\n\n return uniqueBy(\n //\n [...newEndpoints, ...resolvedCurrentEndpoints],\n endpoint => l34EndpointToString(endpoint),\n ) as TEdnpoints[]\n}\n","import { local, remote, type types } from \"@pulumi/command\"\nimport {\n ComponentResource,\n interpolate,\n output,\n type ComponentResourceOptions,\n type Input,\n type InputOrArray,\n type Output,\n} from \"@highstate/pulumi\"\nimport { common, ssh } from \"@highstate/library\"\nimport { l3EndpointToString } from \"./network\"\n\nexport function getServerConnection(\n ssh: Input<ssh.Credentials>,\n): Output<types.input.remote.ConnectionArgs> {\n return output(ssh).apply(ssh => ({\n host: l3EndpointToString(ssh.endpoints[0]),\n port: ssh.endpoints[0].port,\n user: ssh.user,\n password: ssh.password,\n privateKey: ssh.keyPair?.privateKey,\n dialErrorLimit: 3,\n hostKey: ssh.hostKey,\n }))\n}\n\nexport type CommandHost = \"local\" | common.Server\n\nexport type CommandArgs = {\n host: Input<CommandHost>\n create: Input<string>\n update?: Input<string>\n delete?: Input<string>\n logging?: Input<remote.Logging>\n triggers?: InputOrArray<unknown>\n}\n\nexport type TextFileArgs = {\n host: CommandHost\n path: Input<string>\n content: Input<string>\n}\n\nexport class Command extends ComponentResource {\n public readonly command: Output<local.Command | remote.Command>\n\n public readonly stdout: Output<string>\n public readonly stderr: Output<string>\n\n constructor(name: string, args: CommandArgs, opts?: ComponentResourceOptions) {\n super(\"highstate:common:Command\", name, args, opts)\n\n this.command = output(args).apply(args => {\n if (args.host === \"local\") {\n return new local.Command(\n name,\n {\n create: args.create,\n update: args.update,\n delete: args.delete,\n logging: args.logging,\n triggers: args.triggers,\n },\n { ...opts, parent: this },\n )\n }\n\n if (!args.host.ssh) {\n throw new Error(`The host \"${args.host.hostname}\" has no SSH credentials`)\n }\n\n return new remote.Command(\n name,\n {\n connection: getServerConnection(args.host.ssh),\n create: args.create,\n update: args.update,\n delete: args.delete,\n logging: args.logging,\n triggers: args.triggers,\n },\n { ...opts, parent: this },\n )\n })\n\n this.stdout = this.command.stdout\n this.stderr = this.command.stderr\n }\n\n static createTextFile(\n name: string,\n options: TextFileArgs,\n opts?: ComponentResourceOptions,\n ): Output<Command> {\n return output(options).apply(options => {\n const escapedContent = options.content.replace(/\"/g, '\\\\\"')\n\n const command = new Command(\n name,\n {\n host: options.host,\n create: interpolate`mkdir -p $(dirname ${options.path}) && echo \"${escapedContent}\" > ${options.path}`,\n delete: interpolate`rm -rf ${options.path}`,\n },\n opts,\n )\n\n return command\n })\n }\n\n static receiveTextFile(\n name: string,\n options: Omit<TextFileArgs, \"content\">,\n opts?: ComponentResourceOptions,\n ): Output<Command> {\n return output(options).apply(options => {\n const command = new Command(\n name,\n {\n host: options.host,\n create: interpolate`while ! test -f ${options.path}; do sleep 1; done; cat ${options.path}`,\n logging: \"stderr\",\n },\n opts,\n )\n\n return command\n })\n }\n}\n","import type { ArrayPatchMode, dns, network } from \"@highstate/library\"\nimport {\n ComponentResource,\n normalize,\n output,\n Output,\n Resource,\n toPromise,\n type Input,\n type InputArray,\n type InputOrArray,\n type ResourceOptions,\n type Unwrap,\n} from \"@highstate/pulumi\"\nimport { capitalize, groupBy, uniqueBy } from \"remeda\"\nimport { Command, type CommandHost } from \"./command\"\nimport {\n filterEndpoints,\n l34EndpointToString,\n l3EndpointToString,\n parseL3Endpoint,\n type InputL3Endpoint,\n} from \"./network\"\n\nexport type DnsRecordArgs = {\n /**\n * The DNS provider to use.\n */\n provider: Input<dns.Provider>\n\n /**\n * The name of the DNS record.\n * If not provided, the name of the resource will be used.\n */\n name?: Input<string>\n\n /**\n * The type of the DNS record.\n *\n * If not provided, will be automatically detected based on the value.\n */\n type?: Input<string>\n\n /**\n * The value of the DNS record.\n */\n value: Input<InputL3Endpoint>\n\n /**\n * Whether the DNS record is proxied (e.g. to provide DDoS protection).\n *\n * Available only for public IPs and some DNS providers like Cloudflare.\n * If not supported, the DNS provider will ignore this value.\n */\n proxied?: Input<boolean>\n\n /**\n * The TTL of the DNS record.\n *\n * If not provided, the DNS provider will use its default value.\n */\n ttl?: Input<number>\n\n /**\n * The priority of the DNS record.\n *\n * Only used for some DNS record types (e.g. MX).\n */\n priority?: Input<number>\n\n /**\n * Wait for the DNS record to be created/updated at the specified environment(s) before continuing.\n */\n waitAt?: InputOrArray<CommandHost>\n}\n\nexport type ResolvedDnsRecordArgs = Unwrap<Omit<DnsRecordArgs, \"value\" | \"type\">> & {\n /**\n * The value of the DNS record.\n */\n value: string\n\n /**\n * The type of the DNS record.\n */\n type: string\n}\n\nexport type DnsRecordSetArgs = Omit<DnsRecordArgs, \"provider\" | \"value\"> & {\n /**\n * The DNS providers to use to create the DNS records.\n *\n * If multiple providers matched the specified domain, multiple DNS records will be created.\n */\n providers: Input<dns.Provider[]>\n\n /**\n * The value of the DNS record.\n */\n value?: Input<InputL3Endpoint>\n\n /**\n * The values of the DNS records.\n */\n values?: InputArray<InputL3Endpoint>\n}\n\nfunction getTypeByEndpoint(endpoint: network.L3Endpoint): string {\n switch (endpoint.type) {\n case \"ipv4\":\n return \"A\"\n case \"ipv6\":\n return \"AAAA\"\n case \"hostname\":\n return \"CNAME\"\n }\n}\n\nexport abstract class DnsRecord extends ComponentResource {\n /**\n * The underlying dns record resource.\n */\n public readonly dnsRecord: Output<Resource>\n\n /**\n * The wait commands to be executed after the DNS record is created/updated.\n *\n * Use this field as a dependency for other resources.\n */\n public readonly waitCommands: Output<Command[]>\n\n protected constructor(name: string, args: DnsRecordArgs, opts?: ResourceOptions) {\n super(\"highstate:common:DnsRecord\", name, args, opts)\n\n this.dnsRecord = output(args).apply(args => {\n const l3Endpoint = parseL3Endpoint(args.value)\n const type = args.type ?? getTypeByEndpoint(l3Endpoint)\n\n return output(\n this.create(\n name,\n {\n ...args,\n type,\n value: l3EndpointToString(l3Endpoint),\n },\n { ...opts, parent: this },\n ),\n )\n })\n\n this.waitCommands = output(args).apply(args => {\n const waitAt = args.waitAt ? (Array.isArray(args.waitAt) ? args.waitAt : [args.waitAt]) : []\n\n return waitAt.map(host => {\n const hostname = host === \"local\" ? \"local\" : host.hostname\n\n return new Command(\n `${name}-wait-${hostname}`,\n {\n host,\n create: `while ! getent hosts ${args.name} >/dev/null; do echo \"Waiting for DNS record ${args.name} to be created\"; sleep 5; done`,\n triggers: [args.type, args.ttl, args.priority, args.proxied],\n },\n { parent: this },\n )\n })\n })\n }\n\n protected abstract create(\n name: string,\n args: ResolvedDnsRecordArgs,\n opts?: ResourceOptions,\n ): Input<Resource>\n\n static create(name: string, args: DnsRecordArgs, opts?: ResourceOptions): Output<DnsRecord> {\n return output(args).apply(async args => {\n const providerType = args.provider.type\n const implName = `${capitalize(providerType)}DnsRecord`\n const implModule = (await import(`@highstate/${providerType}`)) as Record<string, unknown>\n\n const implClass = implModule[implName] as new (\n name: string,\n args: Unwrap<DnsRecordArgs>,\n opts?: ResourceOptions,\n ) => DnsRecord\n\n return new implClass(name, args, opts)\n })\n }\n}\n\nexport class DnsRecordSet extends ComponentResource {\n /**\n * The underlying dns record resources.\n */\n public readonly dnsRecords: Output<DnsRecord[]>\n\n /**\n * The wait commands to be executed after the DNS records are created/updated.\n */\n public readonly waitCommands: Output<Command[]>\n\n private constructor(name: string, records: Output<DnsRecord[]>, opts?: ResourceOptions) {\n super(\"highstate:common:DnsRecordSet\", name, records, opts)\n\n this.dnsRecords = records\n\n this.waitCommands = records.apply(records =>\n records.flatMap(record => record.waitCommands),\n ) as unknown as Output<Command[]>\n }\n\n static create(name: string, args: DnsRecordSetArgs, opts?: ResourceOptions): DnsRecordSet {\n const records = output(args).apply(args => {\n const recordName = args.name ?? name\n const values = normalize(args.value, args.values)\n\n return output(\n args.providers\n .filter(provider => recordName.endsWith(provider.domain))\n .flatMap(provider => {\n return values.map(value => {\n const l3Endpoint = parseL3Endpoint(value)\n\n return DnsRecord.create(\n `${provider.type}-from-${recordName}-to-${l3EndpointToString(l3Endpoint)}`,\n { name: recordName, ...args, value: l3Endpoint, provider },\n opts,\n )\n })\n }),\n )\n })\n\n return new DnsRecordSet(name, records, opts)\n }\n}\n\n/**\n * Registers the DNS record set for the given endpoints and prepends the corresponding hostname endpoint to the list.\n *\n * Waits for the DNS record set to be created/updated before continuing.\n *\n * Ignores the \"hostname\" endpoints in the list.\n *\n * @param endpoints The list of endpoints to register. Will be modified in place.\n * @param fqdn The FQDN to register the DNS record set for. If not provided, no DNS record set will be created and array will not be modified.\n * @param fqdnEndpointFilter The filter to apply to the endpoints before passing them to the DNS record set. Does not apply to the resulted endpoint list.\n * @param dnsProviders The DNS providers to use to create the DNS records.\n */\nexport async function updateEndpointsWithFqdn<TEndpoint extends network.L34Endpoint>(\n endpoints: Input<TEndpoint[]>,\n fqdn: string | undefined,\n fqdnEndpointFilter: network.EndpointFilter,\n patchMode: ArrayPatchMode,\n dnsProviders: Input<dns.Provider[]>,\n): Promise<{ endpoints: TEndpoint[]; dnsRecordSet: DnsRecordSet | undefined }> {\n const resolvedEndpoints = await toPromise(endpoints)\n\n if (!fqdn) {\n return {\n endpoints: resolvedEndpoints as TEndpoint[],\n dnsRecordSet: undefined,\n }\n }\n\n const filteredEndpoints = filterEndpoints(resolvedEndpoints, fqdnEndpointFilter)\n\n const dnsRecordSet = DnsRecordSet.create(fqdn, {\n providers: dnsProviders,\n values: filteredEndpoints,\n waitAt: \"local\",\n })\n\n const portProtocolGroups = groupBy(filteredEndpoints, endpoint =>\n endpoint.port ? `${endpoint.port}-${endpoint.protocol}` : \"\",\n )\n\n const newEndpoints: TEndpoint[] = []\n\n for (const group of Object.values(portProtocolGroups)) {\n newEndpoints.unshift({\n type: \"hostname\",\n hostname: fqdn,\n visibility: group[0].visibility,\n port: group[0].port,\n protocol: group[0].protocol,\n } as TEndpoint)\n }\n\n await toPromise(\n dnsRecordSet.waitCommands.apply(waitCommands => waitCommands.map(command => command.stdout)),\n )\n\n if (patchMode === \"prepend\") {\n return {\n endpoints: uniqueBy(\n //\n [...newEndpoints, ...(resolvedEndpoints as TEndpoint[])],\n endpoint => l34EndpointToString(endpoint),\n ),\n dnsRecordSet,\n }\n }\n\n return {\n endpoints: newEndpoints,\n dnsRecordSet,\n }\n}\n","import { randomBytes } from \"@noble/hashes/utils\"\nimport { secureMask } from \"micro-key-producer/password.js\"\n\nexport function generatePassword() {\n return secureMask.apply(randomBytes(32)).password\n}\n","import type { common, network, ssh } from \"@highstate/library\"\nimport {\n getOrCreateSecret,\n getUnitInstanceName,\n output,\n Output,\n secret,\n type Input,\n type InstanceTerminal,\n} from \"@highstate/pulumi\"\nimport getKeys, { PrivateExport } from \"micro-key-producer/ssh.js\"\nimport { randomBytes } from \"micro-key-producer/utils.js\"\nimport { local, remote } from \"@pulumi/command\"\nimport { l3EndpointToString, l3ToL4Endpoint, l4EndpointToString } from \"./network\"\n\nexport function createSshTerminal(\n credentials: Input<ssh.Credentials | undefined>,\n): Output<InstanceTerminal | undefined> {\n return output(credentials).apply(credentials => {\n if (!credentials) {\n return undefined\n }\n\n const command = [\"ssh\", \"-tt\", \"-o\", \"UserKnownHostsFile=/known_hosts\"]\n\n // TODO: select best endpoint based on the environment\n const endpoint = credentials.endpoints[0]\n\n command.push(\"-p\", endpoint.port.toString())\n\n if (credentials.keyPair) {\n command.push(\"-i\", \"/private_key\")\n }\n\n command.push(`${credentials.user}@${l3EndpointToString(endpoint)}`)\n\n if (credentials.password) {\n command.unshift(\"sshpass\", \"-f\", \"/password\")\n }\n\n return {\n name: \"ssh\",\n title: `SSH: ${getUnitInstanceName()}`,\n description: \"Connect to the server via SSH\",\n image: \"ghcr.io/exeteres/highstate/terminal-ssh\",\n command,\n\n files: {\n \"/password\": credentials.password,\n\n \"/private_key\": {\n content: credentials.keyPair?.privateKey,\n mode: 0o600,\n },\n\n \"/known_hosts\": {\n content: `${l3EndpointToString(endpoint)} ${credentials.hostKey}`,\n mode: 0o644,\n },\n },\n }\n })\n}\n\nexport function generatePrivateKey(): string {\n const seed = randomBytes(32)\n\n return getKeys(seed).privateKey\n}\n\nexport function privateKeyToKeyPair(privateKeyString: Input<string>): Output<ssh.KeyPair> {\n return output(privateKeyString).apply(privateKeyString => {\n const privateKeyStruct = PrivateExport.decode(privateKeyString)\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const privKey = privateKeyStruct.keys[0].privKey.privKey as Uint8Array\n\n const { fingerprint, publicKey, privateKey } = getKeys(privKey.slice(0, 32))\n\n return output({\n type: \"ed25519\" as const,\n fingerprint,\n publicKey,\n privateKey: secret(privateKey),\n })\n })\n}\n\nexport type SshKeyPairInputs = {\n sshKeyPair?: Input<ssh.KeyPair>\n}\n\nexport type SshKeyPairSecrets = {\n sshPrivateKey?: Input<string>\n}\n\nexport function getOrCreateSshKeyPair(\n inputs: SshKeyPairInputs,\n secrets: Output<SshKeyPairSecrets>,\n): Output<ssh.KeyPair> {\n if (inputs.sshKeyPair) {\n return output(inputs.sshKeyPair)\n }\n\n const privateKey = getOrCreateSecret(secrets, \"sshPrivateKey\", generatePrivateKey)\n\n return privateKey.apply(privateKeyToKeyPair)\n}\n\nexport function createServerEntity(\n fallbackHostname: string,\n endpoint: network.L3Endpoint,\n sshPort = 22,\n sshUser = \"root\",\n sshPassword?: Input<string | undefined>,\n sshPrivateKey?: Input<string>,\n): Output<common.Server> {\n const connection = output({\n host: l3EndpointToString(endpoint),\n port: sshPort,\n user: sshUser,\n password: sshPassword,\n privateKey: sshPrivateKey,\n dialErrorLimit: 3,\n })\n\n const command = new local.Command(\"check-ssh\", {\n create: `nc -zv ${l3EndpointToString(endpoint)} ${sshPort} && echo \"up\" || echo \"down\"`,\n })\n\n return command.stdout.apply(result => {\n if (result === \"down\") {\n return output({\n hostname: fallbackHostname,\n endpoints: [endpoint],\n })\n }\n\n const hostnameResult = new remote.Command(\"hostname\", {\n connection,\n create: \"hostname\",\n triggers: [Date.now()],\n })\n\n const hostKeyResult = new remote.Command(\"host-key\", {\n connection,\n create: \"cat /etc/ssh/ssh_host_ed25519_key.pub\",\n triggers: [Date.now()],\n })\n\n return output({\n endpoints: [endpoint],\n hostname: hostnameResult.stdout.apply(x => x.trim()),\n ssh: {\n endpoints: [l3ToL4Endpoint(endpoint, sshPort)],\n user: sshUser,\n hostKey: hostKeyResult.stdout.apply(x => x.trim()),\n password: sshPassword,\n keyPair: sshPrivateKey ? privateKeyToKeyPair(sshPrivateKey) : undefined,\n },\n })\n })\n}\n"],"mappings":"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","names":["ssh","args","options","ComponentResource","output","toPromise","uniqueBy","ComponentResource","output","args","records","toPromise","uniqueBy","output","randomBytes","local","remote","output","credentials","randomBytes","privateKeyString","local","remote"]}
|