@highstate/common 0.9.18 → 0.9.19
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-YYNV3MVT.js → chunk-WDYIUWYZ.js} +484 -176
- package/dist/chunk-WDYIUWYZ.js.map +1 -0
- package/dist/highstate.manifest.json +12 -8
- package/dist/index.js +1 -1
- package/dist/units/access-point/index.js +16 -0
- package/dist/units/access-point/index.js.map +1 -0
- package/dist/units/databases/existing-mariadb/index.js +17 -0
- package/dist/units/databases/existing-mariadb/index.js.map +1 -0
- package/dist/units/databases/existing-mongodb/index.js +17 -0
- package/dist/units/databases/existing-mongodb/index.js.map +1 -0
- package/dist/units/databases/existing-postgresql/index.js +17 -0
- package/dist/units/databases/existing-postgresql/index.js.map +1 -0
- package/dist/units/dns/record-set/index.js +22 -11
- package/dist/units/dns/record-set/index.js.map +1 -1
- package/dist/units/existing-server/index.js +9 -9
- package/dist/units/existing-server/index.js.map +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 +4 -3
- package/dist/units/ssh/key-pair/index.js.map +1 -1
- package/package.json +61 -8
- package/src/shared/access-point.ts +110 -0
- package/src/shared/command.ts +81 -14
- package/src/shared/dns.ts +150 -90
- package/src/shared/files.ts +23 -18
- package/src/shared/gateway.ts +117 -0
- package/src/shared/impl-ref.ts +123 -0
- package/src/shared/index.ts +4 -0
- package/src/shared/network.ts +39 -25
- package/src/shared/passwords.ts +3 -3
- package/src/shared/ssh.ts +109 -124
- package/src/shared/tls.ts +123 -0
- package/src/units/access-point/index.ts +12 -0
- package/src/units/databases/existing-mariadb/index.ts +14 -0
- package/src/units/databases/existing-mongodb/index.ts +14 -0
- package/src/units/databases/existing-postgresql/index.ts +14 -0
- package/src/units/dns/record-set/index.ts +21 -11
- package/src/units/existing-server/index.ts +9 -15
- package/src/units/ssh/key-pair/index.ts +4 -3
- package/dist/chunk-YYNV3MVT.js.map +0 -1
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../../../src/units/ssh/key-pair/index.ts"],"names":[],"mappings":";;;;AAIA,IAAM,EAAE,IAAA,EAAM,
|
1
|
+
{"version":3,"sources":["../../../../src/units/ssh/key-pair/index.ts"],"names":[],"mappings":";;;;AAIA,IAAM,EAAE,IAAA,EAAM,SAAA,EAAW,SAAQ,GAAI,OAAA,CAAQ,IAAI,OAAO,CAAA;AAExD,IAAM,UAAA,GAAa,SAAA,CAAU,YAAA,EAAc,qBAAqB,CAAA;AAChE,IAAM,OAAA,GAAU,uBAAuB,UAAU,CAAA;AAEjD,IAAO,mBAAQ,OAAA,CAAQ;AAAA,EACrB,OAAA;AAAA,EACA,aAAA,EAAe;AAAA,IACb,IAAA,EAAM;AAAA,MACJ,IAAA,EAAM,GAAG,IAAI,CAAA,IAAA,CAAA;AAAA,MACb,IAAA,EAAM;AAAA,KACR;AAAA,IACA,OAAA,EAAS;AAAA,MACP,IAAA,EAAM,UAAA;AAAA,MACN,OAAO,OAAA,CAAQ;AAAA;AACjB,GACF;AAAA,EACA,aAAA,EAAe;AAAA,IACb,aAAa,OAAA,CAAQ,WAAA;AAAA,IACrB,WAAW,OAAA,CAAQ;AAAA;AAEvB,CAAC","file":"index.js","sourcesContent":["import { ssh } from \"@highstate/library\"\nimport { forUnit } from \"@highstate/pulumi\"\nimport { generateSshPrivateKey, sshPrivateKeyToKeyPair } from \"../../../shared\"\n\nconst { name, getSecret, outputs } = forUnit(ssh.keyPair)\n\nconst privateKey = getSecret(\"privateKey\", generateSshPrivateKey)\nconst keyPair = sshPrivateKeyToKeyPair(privateKey)\n\nexport default outputs({\n keyPair,\n publicKeyFile: {\n meta: {\n name: `${name}.pub`,\n mode: 0o644,\n },\n content: {\n type: \"embedded\",\n value: keyPair.publicKey,\n },\n },\n $statusFields: {\n fingerprint: keyPair.fingerprint,\n publicKey: keyPair.publicKey,\n },\n})\n"]}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@highstate/common",
|
3
|
-
"version": "0.9.
|
3
|
+
"version": "0.9.19",
|
4
4
|
"type": "module",
|
5
5
|
"files": [
|
6
6
|
"dist",
|
@@ -11,6 +11,54 @@
|
|
11
11
|
".": {
|
12
12
|
"mode": "manual",
|
13
13
|
"version": "1"
|
14
|
+
},
|
15
|
+
"./units/access-point": {
|
16
|
+
"mode": "manual",
|
17
|
+
"version": "1"
|
18
|
+
},
|
19
|
+
"./units/dns/record-set": {
|
20
|
+
"mode": "manual",
|
21
|
+
"version": "1"
|
22
|
+
},
|
23
|
+
"./units/network/l3-endpoint": {
|
24
|
+
"mode": "manual",
|
25
|
+
"version": "1"
|
26
|
+
},
|
27
|
+
"./units/network/l4-endpoint": {
|
28
|
+
"mode": "manual",
|
29
|
+
"version": "1"
|
30
|
+
},
|
31
|
+
"./units/existing-server": {
|
32
|
+
"mode": "manual",
|
33
|
+
"version": "1"
|
34
|
+
},
|
35
|
+
"./units/ssh/key-pair": {
|
36
|
+
"mode": "manual",
|
37
|
+
"version": "1"
|
38
|
+
},
|
39
|
+
"./units/script": {
|
40
|
+
"mode": "manual",
|
41
|
+
"version": "1"
|
42
|
+
},
|
43
|
+
"./units/server-dns": {
|
44
|
+
"mode": "manual",
|
45
|
+
"version": "1"
|
46
|
+
},
|
47
|
+
"./units/server-patch": {
|
48
|
+
"mode": "manual",
|
49
|
+
"version": "1"
|
50
|
+
},
|
51
|
+
"./units/databases/existing-mariadb": {
|
52
|
+
"mode": "manual",
|
53
|
+
"version": "1"
|
54
|
+
},
|
55
|
+
"./units/databases/existing-postgresql": {
|
56
|
+
"mode": "manual",
|
57
|
+
"version": "1"
|
58
|
+
},
|
59
|
+
"./units/databases/existing-mongodb": {
|
60
|
+
"mode": "manual",
|
61
|
+
"version": "1"
|
14
62
|
}
|
15
63
|
}
|
16
64
|
},
|
@@ -19,6 +67,7 @@
|
|
19
67
|
"types": "./src/index.ts",
|
20
68
|
"default": "./dist/index.js"
|
21
69
|
},
|
70
|
+
"./units/access-point": "./dist/units/access-point/index.js",
|
22
71
|
"./units/dns/record-set": "./dist/units/dns/record-set/index.js",
|
23
72
|
"./units/network/l3-endpoint": "./dist/units/network/l3-endpoint/index.js",
|
24
73
|
"./units/network/l4-endpoint": "./dist/units/network/l4-endpoint/index.js",
|
@@ -26,7 +75,10 @@
|
|
26
75
|
"./units/ssh/key-pair": "./dist/units/ssh/key-pair/index.js",
|
27
76
|
"./units/script": "./dist/units/script/index.js",
|
28
77
|
"./units/server-dns": "./dist/units/server-dns/index.js",
|
29
|
-
"./units/server-patch": "./dist/units/server-patch/index.js"
|
78
|
+
"./units/server-patch": "./dist/units/server-patch/index.js",
|
79
|
+
"./units/databases/existing-mariadb": "./dist/units/databases/existing-mariadb/index.js",
|
80
|
+
"./units/databases/existing-postgresql": "./dist/units/databases/existing-postgresql/index.js",
|
81
|
+
"./units/databases/existing-mongodb": "./dist/units/databases/existing-mongodb/index.js"
|
30
82
|
},
|
31
83
|
"publishConfig": {
|
32
84
|
"access": "public"
|
@@ -36,9 +88,9 @@
|
|
36
88
|
"update-images": "../../scripts/update-images.sh ./assets/images.json"
|
37
89
|
},
|
38
90
|
"dependencies": {
|
39
|
-
"@highstate/contract": "^0.9.
|
40
|
-
"@highstate/library": "^0.9.
|
41
|
-
"@highstate/pulumi": "^0.9.
|
91
|
+
"@highstate/contract": "^0.9.19",
|
92
|
+
"@highstate/library": "^0.9.19",
|
93
|
+
"@highstate/pulumi": "^0.9.19",
|
42
94
|
"@noble/hashes": "^1.7.1",
|
43
95
|
"@pulumi/command": "^1.0.2",
|
44
96
|
"micro-key-producer": "^0.7.3",
|
@@ -48,9 +100,10 @@
|
|
48
100
|
"unzipper": "^0.12.3"
|
49
101
|
},
|
50
102
|
"devDependencies": {
|
51
|
-
"@highstate/cli": "^0.9.
|
103
|
+
"@highstate/cli": "^0.9.19",
|
52
104
|
"@types/tar": "^6.1.13",
|
53
|
-
"@types/unzipper": "^0.10.11"
|
105
|
+
"@types/unzipper": "^0.10.11",
|
106
|
+
"type-fest": "^4.41.0"
|
54
107
|
},
|
55
|
-
"gitHead": "
|
108
|
+
"gitHead": "e77d292335556c6e5b6275acda1a3d1609d786a1"
|
56
109
|
}
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import type { common } from "@highstate/library"
|
2
|
+
import type { Except } from "type-fest"
|
3
|
+
import {
|
4
|
+
ComponentResource,
|
5
|
+
type ComponentResourceOptions,
|
6
|
+
type Input,
|
7
|
+
type Output,
|
8
|
+
output,
|
9
|
+
toPromise,
|
10
|
+
} from "@highstate/pulumi"
|
11
|
+
import { DnsRecordSet } from "./dns"
|
12
|
+
import { GatewayRoute, type GatewayRouteSpec } from "./gateway"
|
13
|
+
import { TlsCertificate } from "./tls"
|
14
|
+
|
15
|
+
export type AccessPointRouteArgs = Except<GatewayRouteSpec, "nativeData"> & {
|
16
|
+
/**
|
17
|
+
* The access point to use to expose the route.
|
18
|
+
*/
|
19
|
+
accessPoint: Input<common.AccessPoint>
|
20
|
+
|
21
|
+
/**
|
22
|
+
* The native data to pass to the gateway route implementation.
|
23
|
+
*/
|
24
|
+
gatewayNativeData?: unknown
|
25
|
+
|
26
|
+
/**
|
27
|
+
* The native data to pass to the tls ceertificate implementation.
|
28
|
+
*/
|
29
|
+
tlsCertificateNativeData?: unknown
|
30
|
+
}
|
31
|
+
|
32
|
+
export class AccessPointRoute extends ComponentResource {
|
33
|
+
/**
|
34
|
+
* The created gateway route.
|
35
|
+
*/
|
36
|
+
readonly route: GatewayRoute
|
37
|
+
|
38
|
+
/**
|
39
|
+
* The DNS record set created for the route.
|
40
|
+
*
|
41
|
+
* May be shared between multiple routes with the same FQDN.
|
42
|
+
*/
|
43
|
+
readonly dnsRecordSet?: Output<DnsRecordSet | undefined>
|
44
|
+
|
45
|
+
/**
|
46
|
+
* The TLS certificate created for the route.
|
47
|
+
*
|
48
|
+
* May be shared between multiple routes with the same FQDN.
|
49
|
+
*/
|
50
|
+
readonly tlsCertificate?: Output<TlsCertificate | undefined>
|
51
|
+
|
52
|
+
constructor(name: string, args: AccessPointRouteArgs, opts?: ComponentResourceOptions) {
|
53
|
+
super("highstate:common:AccessPointRoute", name, args, opts)
|
54
|
+
|
55
|
+
// 1. create TLS certificate if the route is HTTPS and the access point has TLS issuers
|
56
|
+
if (args.fqdn && args.type === "http" && !args.insecure) {
|
57
|
+
this.tlsCertificate = output(args.accessPoint).apply(accessPoint => {
|
58
|
+
if (accessPoint.tlsIssuers.length === 0) {
|
59
|
+
return undefined
|
60
|
+
}
|
61
|
+
|
62
|
+
return TlsCertificate.createOnce(
|
63
|
+
name,
|
64
|
+
{
|
65
|
+
issuers: accessPoint.tlsIssuers,
|
66
|
+
dnsNames: args.fqdn ? [args.fqdn] : [],
|
67
|
+
nativeData: args.tlsCertificateNativeData,
|
68
|
+
},
|
69
|
+
{ ...opts, parent: this },
|
70
|
+
)
|
71
|
+
})
|
72
|
+
}
|
73
|
+
|
74
|
+
// 2. create the route and resolve the gateway endpoints
|
75
|
+
this.route = new GatewayRoute(
|
76
|
+
name,
|
77
|
+
{
|
78
|
+
...args,
|
79
|
+
gateway: output(args.accessPoint).gateway,
|
80
|
+
tlsCertificate: this.tlsCertificate,
|
81
|
+
nativeData: args.gatewayNativeData,
|
82
|
+
},
|
83
|
+
{ ...opts, parent: this },
|
84
|
+
)
|
85
|
+
|
86
|
+
// 3. register DNS records if FQDN is provided and the access point has DNS providers
|
87
|
+
if (args.fqdn) {
|
88
|
+
this.dnsRecordSet = output(args.accessPoint).apply(async accessPoint => {
|
89
|
+
if (accessPoint.dnsProviders.length === 0) {
|
90
|
+
return undefined
|
91
|
+
}
|
92
|
+
|
93
|
+
const fqdn = await toPromise(args.fqdn)
|
94
|
+
if (!fqdn) {
|
95
|
+
return undefined
|
96
|
+
}
|
97
|
+
|
98
|
+
return DnsRecordSet.createOnce(
|
99
|
+
fqdn,
|
100
|
+
{
|
101
|
+
providers: output(args.accessPoint).dnsProviders,
|
102
|
+
values: this.route.endpoints,
|
103
|
+
waitAt: "local",
|
104
|
+
},
|
105
|
+
{ ...opts, parent: this },
|
106
|
+
)
|
107
|
+
})
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
package/src/shared/command.ts
CHANGED
@@ -7,13 +7,13 @@ import {
|
|
7
7
|
toPromise,
|
8
8
|
type ComponentResourceOptions,
|
9
9
|
type Input,
|
10
|
-
type InputMap,
|
11
10
|
type InputOrArray,
|
12
11
|
type Output,
|
13
12
|
} from "@highstate/pulumi"
|
14
|
-
import { common, ssh } from "@highstate/library"
|
13
|
+
import type { common, ssh } from "@highstate/library"
|
15
14
|
import { flat } from "remeda"
|
16
15
|
import { l3EndpointToString } from "./network"
|
16
|
+
import { sha256 } from "@noble/hashes/sha2"
|
17
17
|
|
18
18
|
/**
|
19
19
|
* Creates a connection object for the given SSH credentials.
|
@@ -22,7 +22,7 @@ import { l3EndpointToString } from "./network"
|
|
22
22
|
* @returns An output connection object for Pulumi remote commands.
|
23
23
|
*/
|
24
24
|
export function getServerConnection(
|
25
|
-
ssh: Input<ssh.
|
25
|
+
ssh: Input<ssh.Connection>,
|
26
26
|
): Output<types.input.remote.ConnectionArgs> {
|
27
27
|
return output(ssh).apply(ssh => ({
|
28
28
|
host: l3EndpointToString(ssh.endpoints[0]),
|
@@ -35,7 +35,7 @@ export function getServerConnection(
|
|
35
35
|
}))
|
36
36
|
}
|
37
37
|
|
38
|
-
export type CommandHost = "local" | Input<common.Server>
|
38
|
+
export type CommandHost = "local" | Input<common.Server>
|
39
39
|
export type CommandRunMode = "auto" | "prefer-host"
|
40
40
|
|
41
41
|
export type CommandArgs = {
|
@@ -83,6 +83,16 @@ export type CommandArgs = {
|
|
83
83
|
*/
|
84
84
|
triggers?: InputOrArray<unknown>
|
85
85
|
|
86
|
+
/**
|
87
|
+
* The update triggers for the command.
|
88
|
+
*
|
89
|
+
* Unlike `triggers`, which replace the entire resource on change, these will only trigger the `update` command.
|
90
|
+
*
|
91
|
+
* Under the hood, it is implemented using a hash of the provided values and passing it as environment variable.
|
92
|
+
* It is recommended to pass only primitive values (strings, numbers, booleans) or small objects/arrays for proper serialization.
|
93
|
+
*/
|
94
|
+
updateTriggers?: InputOrArray<unknown>
|
95
|
+
|
86
96
|
/**
|
87
97
|
* The working directory for the command.
|
88
98
|
*
|
@@ -93,7 +103,7 @@ export type CommandArgs = {
|
|
93
103
|
/**
|
94
104
|
* The environment variables to set for the command.
|
95
105
|
*/
|
96
|
-
environment?: Input<
|
106
|
+
environment?: Input<Record<string, Input<string>>>
|
97
107
|
|
98
108
|
/**
|
99
109
|
* The run mode for the command.
|
@@ -168,12 +178,50 @@ function wrapWithWorkDir(dir?: Input<string>) {
|
|
168
178
|
return (command: string) => interpolate`cd "${dir}" && ${command}`
|
169
179
|
}
|
170
180
|
|
181
|
+
function wrapWithEnvironment(environment?: Input<Record<string, Input<string>>>) {
|
182
|
+
if (!environment) {
|
183
|
+
return (command: string) => output(command)
|
184
|
+
}
|
185
|
+
|
186
|
+
return (command: string) =>
|
187
|
+
output({ command, environment }).apply(({ command, environment }) => {
|
188
|
+
if (!environment || Object.keys(environment).length === 0) {
|
189
|
+
return command
|
190
|
+
}
|
191
|
+
|
192
|
+
const envExport = Object.entries(environment)
|
193
|
+
.map(([key, value]) => `export ${key}="${value}"`)
|
194
|
+
.join(" && ")
|
195
|
+
|
196
|
+
return `${envExport} && ${command}`
|
197
|
+
})
|
198
|
+
}
|
199
|
+
|
171
200
|
function wrapWithWaitFor(timeout: Input<number> = 300, interval: Input<number> = 5) {
|
172
201
|
return (command: string | string[]) =>
|
173
202
|
// TOD: escape the command
|
174
203
|
interpolate`timeout ${timeout} bash -c 'while ! ${createCommand(command)}; do sleep ${interval}; done'`
|
175
204
|
}
|
176
205
|
|
206
|
+
function applyUpdateTriggers(
|
207
|
+
env: Input<Record<string, Input<string>>> | undefined,
|
208
|
+
triggers: InputOrArray<unknown> | undefined,
|
209
|
+
) {
|
210
|
+
return output({ env, triggers }).apply(({ env, triggers }) => {
|
211
|
+
if (!triggers) {
|
212
|
+
return env
|
213
|
+
}
|
214
|
+
|
215
|
+
const hash = sha256(JSON.stringify(triggers))
|
216
|
+
const hashHex = Buffer.from(hash).toString("hex")
|
217
|
+
|
218
|
+
return {
|
219
|
+
...env,
|
220
|
+
HIGHSTATE_UPDATE_TRIGGER_HASH: hashHex,
|
221
|
+
}
|
222
|
+
})
|
223
|
+
}
|
224
|
+
|
177
225
|
export class Command extends ComponentResource {
|
178
226
|
public readonly stdout: Output<string>
|
179
227
|
public readonly stderr: Output<string>
|
@@ -181,6 +229,11 @@ export class Command extends ComponentResource {
|
|
181
229
|
constructor(name: string, args: CommandArgs, opts?: ComponentResourceOptions) {
|
182
230
|
super("highstate:common:Command", name, args, opts)
|
183
231
|
|
232
|
+
const environment = applyUpdateTriggers(
|
233
|
+
args.environment,
|
234
|
+
args.updateTriggers,
|
235
|
+
) as local.CommandArgs["environment"]
|
236
|
+
|
184
237
|
const command =
|
185
238
|
args.host === "local"
|
186
239
|
? new local.Command(
|
@@ -192,7 +245,7 @@ export class Command extends ComponentResource {
|
|
192
245
|
logging: args.logging,
|
193
246
|
triggers: args.triggers ? output(args.triggers).apply(flat) : undefined,
|
194
247
|
dir: args.cwd ?? homedir(),
|
195
|
-
environment
|
248
|
+
environment,
|
196
249
|
stdin: args.stdin,
|
197
250
|
},
|
198
251
|
{ ...opts, parent: this },
|
@@ -201,10 +254,6 @@ export class Command extends ComponentResource {
|
|
201
254
|
name,
|
202
255
|
{
|
203
256
|
connection: output(args.host).apply(server => {
|
204
|
-
if ("host" in server) {
|
205
|
-
return output(server)
|
206
|
-
}
|
207
|
-
|
208
257
|
if (!server.ssh) {
|
209
258
|
throw new Error(`The server "${server.hostname}" has no SSH credentials`)
|
210
259
|
}
|
@@ -212,26 +261,44 @@ export class Command extends ComponentResource {
|
|
212
261
|
return getServerConnection(server.ssh)
|
213
262
|
}),
|
214
263
|
|
215
|
-
create: output(args.create)
|
264
|
+
create: output(args.create)
|
265
|
+
.apply(createCommand)
|
266
|
+
.apply(wrapWithWorkDir(args.cwd))
|
267
|
+
.apply(wrapWithEnvironment(environment)),
|
216
268
|
|
217
269
|
update: args.update
|
218
|
-
? output(args.update)
|
270
|
+
? output(args.update)
|
271
|
+
.apply(createCommand)
|
272
|
+
.apply(wrapWithWorkDir(args.cwd))
|
273
|
+
.apply(wrapWithEnvironment(environment))
|
219
274
|
: undefined,
|
220
275
|
|
221
276
|
delete: args.delete
|
222
|
-
? output(args.delete)
|
277
|
+
? output(args.delete)
|
278
|
+
.apply(createCommand)
|
279
|
+
.apply(wrapWithWorkDir(args.cwd))
|
280
|
+
.apply(wrapWithEnvironment(environment))
|
223
281
|
: undefined,
|
224
282
|
|
225
283
|
logging: args.logging,
|
226
284
|
triggers: args.triggers ? output(args.triggers).apply(flat) : undefined,
|
227
285
|
stdin: args.stdin,
|
228
|
-
|
286
|
+
|
287
|
+
addPreviousOutputInEnv: false,
|
288
|
+
|
289
|
+
// TODO: does not work if server do not define AcceptEnv
|
290
|
+
// environment,
|
229
291
|
},
|
230
292
|
{ ...opts, parent: this },
|
231
293
|
)
|
232
294
|
|
233
295
|
this.stdout = command.stdout
|
234
296
|
this.stderr = command.stderr
|
297
|
+
|
298
|
+
this.registerOutputs({
|
299
|
+
stdout: this.stdout,
|
300
|
+
stderr: this.stderr,
|
301
|
+
})
|
235
302
|
}
|
236
303
|
|
237
304
|
/**
|