@usine/executor 0.2.1 → 0.4.0
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/LICENSE +661 -21
- package/README.md +212 -37
- package/dist/child-command.d.ts +5 -8
- package/dist/child-command.d.ts.map +1 -1
- package/dist/child-command.js +10 -14
- package/dist/child-command.js.map +1 -1
- package/dist/child-environment.d.ts +19 -0
- package/dist/child-environment.d.ts.map +1 -0
- package/dist/child-environment.js +18 -0
- package/dist/child-environment.js.map +1 -0
- package/dist/claude-login-lock.d.ts +9 -0
- package/dist/claude-login-lock.d.ts.map +1 -0
- package/dist/claude-login-lock.js +15 -0
- package/dist/claude-login-lock.js.map +1 -0
- package/dist/cloudflare/client.d.ts +50 -0
- package/dist/cloudflare/client.d.ts.map +1 -0
- package/dist/cloudflare/client.js +108 -0
- package/dist/cloudflare/client.js.map +1 -0
- package/dist/cloudflare/cloudflare.d.ts +17 -0
- package/dist/cloudflare/cloudflare.d.ts.map +1 -0
- package/dist/cloudflare/cloudflare.js +704 -0
- package/dist/cloudflare/cloudflare.js.map +1 -0
- package/dist/cloudflare/model.d.ts +42 -0
- package/dist/cloudflare/model.d.ts.map +1 -0
- package/dist/cloudflare/model.js +2 -0
- package/dist/cloudflare/model.js.map +1 -0
- package/dist/cloudflare/shell/environment.d.ts +12 -0
- package/dist/cloudflare/shell/environment.d.ts.map +1 -0
- package/dist/cloudflare/shell/environment.js +12 -0
- package/dist/cloudflare/shell/environment.js.map +1 -0
- package/dist/cloudflare/shell/shell.d.ts +2 -0
- package/dist/cloudflare/shell/shell.d.ts.map +1 -0
- package/dist/cloudflare/shell/shell.js +155 -0
- package/dist/cloudflare/shell/shell.js.map +1 -0
- package/dist/credential-environment.d.ts +20 -0
- package/dist/credential-environment.d.ts.map +1 -0
- package/dist/credential-environment.js +20 -0
- package/dist/credential-environment.js.map +1 -0
- package/dist/durable-session.d.ts +81 -0
- package/dist/durable-session.d.ts.map +1 -0
- package/dist/durable-session.js +550 -0
- package/dist/durable-session.js.map +1 -0
- package/dist/exe-dev/commands.d.ts +23 -13
- package/dist/exe-dev/commands.d.ts.map +1 -1
- package/dist/exe-dev/commands.js +11 -57
- package/dist/exe-dev/commands.js.map +1 -1
- package/dist/exe-dev/exe-dev.d.ts +9 -3
- package/dist/exe-dev/exe-dev.d.ts.map +1 -1
- package/dist/exe-dev/exe-dev.js +61 -128
- package/dist/exe-dev/exe-dev.js.map +1 -1
- package/dist/exe-dev/grants.d.ts.map +1 -1
- package/dist/exe-dev/grants.js +35 -15
- package/dist/exe-dev/grants.js.map +1 -1
- package/dist/exe-dev/model.d.ts +15 -19
- package/dist/exe-dev/model.d.ts.map +1 -1
- package/dist/exe-dev/model.js +9 -12
- package/dist/exe-dev/model.js.map +1 -1
- package/dist/exe-dev/preflight.d.ts +8 -4
- package/dist/exe-dev/preflight.d.ts.map +1 -1
- package/dist/exe-dev/preflight.js +26 -35
- package/dist/exe-dev/preflight.js.map +1 -1
- package/dist/executor.d.ts +292 -44
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +187 -19
- package/dist/executor.js.map +1 -1
- package/dist/guard.d.ts +57 -7
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +274 -59
- package/dist/guard.js.map +1 -1
- package/dist/harness-preflight.d.ts +27 -5
- package/dist/harness-preflight.d.ts.map +1 -1
- package/dist/harness-preflight.js +32 -31
- package/dist/harness-preflight.js.map +1 -1
- package/dist/keyed-lock.d.ts +2 -3
- package/dist/keyed-lock.d.ts.map +1 -1
- package/dist/keyed-lock.js +2 -3
- package/dist/keyed-lock.js.map +1 -1
- package/dist/leftovers.d.ts +9 -0
- package/dist/leftovers.d.ts.map +1 -0
- package/dist/leftovers.js +14 -0
- package/dist/leftovers.js.map +1 -0
- package/dist/local/credentials.d.ts +21 -0
- package/dist/local/credentials.d.ts.map +1 -0
- package/dist/local/credentials.js +39 -0
- package/dist/local/credentials.js.map +1 -0
- package/dist/local/leftovers.d.ts +8 -0
- package/dist/local/leftovers.d.ts.map +1 -0
- package/dist/local/leftovers.js +23 -0
- package/dist/local/leftovers.js.map +1 -0
- package/dist/local/local.d.ts +17 -11
- package/dist/local/local.d.ts.map +1 -1
- package/dist/local/local.js +205 -132
- package/dist/local/local.js.map +1 -1
- package/dist/memory-preflight.d.ts +27 -0
- package/dist/memory-preflight.d.ts.map +1 -0
- package/dist/memory-preflight.js +83 -0
- package/dist/memory-preflight.js.map +1 -0
- package/dist/pre-push-hook.d.ts +20 -0
- package/dist/pre-push-hook.d.ts.map +1 -0
- package/dist/pre-push-hook.js +33 -0
- package/dist/pre-push-hook.js.map +1 -0
- package/dist/prepared-attempt.d.ts +41 -3
- package/dist/prepared-attempt.d.ts.map +1 -1
- package/dist/prepared-attempt.js +109 -31
- package/dist/prepared-attempt.js.map +1 -1
- package/dist/provision.d.ts +129 -15
- package/dist/provision.d.ts.map +1 -1
- package/dist/provision.js +428 -69
- package/dist/provision.js.map +1 -1
- package/dist/remote/commands.d.ts +51 -0
- package/dist/remote/commands.d.ts.map +1 -0
- package/dist/remote/commands.js +94 -0
- package/dist/remote/commands.js.map +1 -0
- package/dist/remote/container.d.ts +90 -0
- package/dist/remote/container.d.ts.map +1 -0
- package/dist/remote/container.js +480 -0
- package/dist/remote/container.js.map +1 -0
- package/dist/remote/leftovers.d.ts +14 -0
- package/dist/remote/leftovers.d.ts.map +1 -0
- package/dist/remote/leftovers.js +22 -0
- package/dist/remote/leftovers.js.map +1 -0
- package/dist/remote/liveness.d.ts +33 -0
- package/dist/remote/liveness.d.ts.map +1 -0
- package/dist/remote/liveness.js +196 -0
- package/dist/remote/liveness.js.map +1 -0
- package/dist/remote/machine.d.ts +110 -0
- package/dist/remote/machine.d.ts.map +1 -0
- package/dist/remote/machine.js +2 -0
- package/dist/remote/machine.js.map +1 -0
- package/dist/remote/remote.d.ts +28 -0
- package/dist/remote/remote.d.ts.map +1 -0
- package/dist/remote/remote.js +556 -0
- package/dist/remote/remote.js.map +1 -0
- package/dist/remote/sweep.d.ts +17 -0
- package/dist/remote/sweep.d.ts.map +1 -0
- package/dist/remote/sweep.js +117 -0
- package/dist/remote/sweep.js.map +1 -0
- package/dist/renewable-credential.d.ts +17 -0
- package/dist/renewable-credential.d.ts.map +1 -0
- package/dist/renewable-credential.js +58 -0
- package/dist/renewable-credential.js.map +1 -0
- package/dist/salvage.d.ts +38 -0
- package/dist/salvage.d.ts.map +1 -0
- package/dist/salvage.js +198 -0
- package/dist/salvage.js.map +1 -0
- package/dist/ssh/api-keys.d.ts +36 -0
- package/dist/ssh/api-keys.d.ts.map +1 -0
- package/dist/ssh/api-keys.js +72 -0
- package/dist/ssh/api-keys.js.map +1 -0
- package/dist/ssh/codex-homes.d.ts +21 -0
- package/dist/ssh/codex-homes.d.ts.map +1 -0
- package/dist/ssh/codex-homes.js +78 -0
- package/dist/ssh/codex-homes.js.map +1 -0
- package/dist/ssh/container.d.ts +32 -0
- package/dist/ssh/container.d.ts.map +1 -0
- package/dist/ssh/container.js +282 -0
- package/dist/ssh/container.js.map +1 -0
- package/dist/ssh/credentials.d.ts +43 -0
- package/dist/ssh/credentials.d.ts.map +1 -0
- package/dist/ssh/credentials.js +86 -0
- package/dist/ssh/credentials.js.map +1 -0
- package/dist/ssh/gateway/admin.d.ts +45 -0
- package/dist/ssh/gateway/admin.d.ts.map +1 -0
- package/dist/ssh/gateway/admin.js +129 -0
- package/dist/ssh/gateway/admin.js.map +1 -0
- package/dist/ssh/gateway/attachments.d.ts +31 -0
- package/dist/ssh/gateway/attachments.d.ts.map +1 -0
- package/dist/ssh/gateway/attachments.js +168 -0
- package/dist/ssh/gateway/attachments.js.map +1 -0
- package/dist/ssh/gateway/environment.d.ts +71 -0
- package/dist/ssh/gateway/environment.d.ts.map +1 -0
- package/dist/ssh/gateway/environment.js +148 -0
- package/dist/ssh/gateway/environment.js.map +1 -0
- package/dist/ssh/gateway/forward.d.ts +74 -0
- package/dist/ssh/gateway/forward.d.ts.map +1 -0
- package/dist/ssh/gateway/forward.js +247 -0
- package/dist/ssh/gateway/forward.js.map +1 -0
- package/dist/ssh/gateway/preflight.d.ts +67 -0
- package/dist/ssh/gateway/preflight.d.ts.map +1 -0
- package/dist/ssh/gateway/preflight.js +320 -0
- package/dist/ssh/gateway/preflight.js.map +1 -0
- package/dist/ssh/limits.d.ts +60 -0
- package/dist/ssh/limits.d.ts.map +1 -0
- package/dist/ssh/limits.js +84 -0
- package/dist/ssh/limits.js.map +1 -0
- package/dist/ssh/model.d.ts +153 -0
- package/dist/ssh/model.d.ts.map +1 -0
- package/dist/ssh/model.js +2 -0
- package/dist/ssh/model.js.map +1 -0
- package/dist/ssh/preflight.d.ts +35 -0
- package/dist/ssh/preflight.d.ts.map +1 -0
- package/dist/ssh/preflight.js +87 -0
- package/dist/ssh/preflight.js.map +1 -0
- package/dist/ssh/provisioner/boat.d.ts +33 -0
- package/dist/ssh/provisioner/boat.d.ts.map +1 -0
- package/dist/ssh/provisioner/boat.js +247 -0
- package/dist/ssh/provisioner/boat.js.map +1 -0
- package/dist/ssh/provisioner/hetzner.d.ts +32 -0
- package/dist/ssh/provisioner/hetzner.d.ts.map +1 -0
- package/dist/ssh/provisioner/hetzner.js +337 -0
- package/dist/ssh/provisioner/hetzner.js.map +1 -0
- package/dist/ssh/provisioner/host-key.d.ts +11 -0
- package/dist/ssh/provisioner/host-key.d.ts.map +1 -0
- package/dist/ssh/provisioner/host-key.js +57 -0
- package/dist/ssh/provisioner/host-key.js.map +1 -0
- package/dist/ssh/provisioner/http.d.ts +45 -0
- package/dist/ssh/provisioner/http.d.ts.map +1 -0
- package/dist/ssh/provisioner/http.js +72 -0
- package/dist/ssh/provisioner/http.js.map +1 -0
- package/dist/ssh/provisioner/machines.d.ts +48 -0
- package/dist/ssh/provisioner/machines.d.ts.map +1 -0
- package/dist/ssh/provisioner/machines.js +686 -0
- package/dist/ssh/provisioner/machines.js.map +1 -0
- package/dist/ssh/provisioner/model.d.ts +92 -0
- package/dist/ssh/provisioner/model.d.ts.map +1 -0
- package/dist/ssh/provisioner/model.js +37 -0
- package/dist/ssh/provisioner/model.js.map +1 -0
- package/dist/ssh/ssh.d.ts +23 -0
- package/dist/ssh/ssh.d.ts.map +1 -0
- package/dist/ssh/ssh.js +145 -0
- package/dist/ssh/ssh.js.map +1 -0
- package/dist/ssh/worker-records.d.ts +18 -0
- package/dist/ssh/worker-records.d.ts.map +1 -0
- package/dist/ssh/worker-records.js +100 -0
- package/dist/ssh/worker-records.js.map +1 -0
- package/dist/ssh/workers.d.ts +80 -0
- package/dist/ssh/workers.d.ts.map +1 -0
- package/dist/ssh/workers.js +247 -0
- package/dist/ssh/workers.js.map +1 -0
- package/package.json +25 -9
- package/dist/exe-dev/channel.d.ts +0 -12
- package/dist/exe-dev/channel.d.ts.map +0 -1
- package/dist/exe-dev/channel.js +0 -42
- package/dist/exe-dev/channel.js.map +0 -1
- package/dist/exe-dev/shell.d.ts +0 -12
- package/dist/exe-dev/shell.d.ts.map +0 -1
- package/dist/exe-dev/shell.js +0 -31
- package/dist/exe-dev/shell.js.map +0 -1
- package/dist/exe-dev/sweep.d.ts +0 -10
- package/dist/exe-dev/sweep.d.ts.map +0 -1
- package/dist/exe-dev/sweep.js +0 -117
- package/dist/exe-dev/sweep.js.map +0 -1
- package/dist/invalid-frame.d.ts +0 -17
- package/dist/invalid-frame.d.ts.map +0 -1
- package/dist/invalid-frame.js +0 -111
- package/dist/invalid-frame.js.map +0 -1
- package/dist/run-host-session.d.ts +0 -27
- package/dist/run-host-session.d.ts.map +0 -1
- package/dist/run-host-session.js +0 -303
- package/dist/run-host-session.js.map +0 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type WorkerName } from "@usine/core/worker";
|
|
2
|
+
import { Effect, Option } from "effect";
|
|
3
|
+
import { MachineUnavailableError, WorkerCapabilityError } from "../../executor.ts";
|
|
4
|
+
import type { HetznerProvisioner } from "../model.ts";
|
|
5
|
+
import { type ListedMachine, type MachineTags, ProviderApiError } from "./model.ts";
|
|
6
|
+
/** The Hetzner Cloud API. */
|
|
7
|
+
export declare const HETZNER_API = "https://api.hetzner.cloud/v1";
|
|
8
|
+
/**
|
|
9
|
+
* Returns the Hetzner Cloud driver for `worker`'s servers (#550). Each server is created from
|
|
10
|
+
* `settings.image` with the project's SSH key, and labeled with the instance, Worker, Run,
|
|
11
|
+
* creating process, and Attempt. With `privateOnly`, a server gets no public address and is
|
|
12
|
+
* reached at its address on `settings.network`, through the Gateway.
|
|
13
|
+
*/
|
|
14
|
+
export declare const hetznerDriverFor: (worker: WorkerName, settings: HetznerProvisioner, privateOnly: boolean, base?: string) => Effect.Effect<{
|
|
15
|
+
provider: "hetzner";
|
|
16
|
+
verify: (_worker: string & import("effect/Brand").Brand<"WorkerName">, maxConcurrentAttempts: number) => Effect.Effect<{
|
|
17
|
+
warnings: string[];
|
|
18
|
+
}, WorkerCapabilityError, never>;
|
|
19
|
+
create: (_name: string, _tags: MachineTags) => Effect.Effect<{
|
|
20
|
+
id: string & import("effect/Brand").Brand<"MachineId">;
|
|
21
|
+
hostKey: Option.Option<string>;
|
|
22
|
+
}, MachineUnavailableError, never>;
|
|
23
|
+
endpoint: (id: string & import("effect/Brand").Brand<"MachineId">, interval: Effect.Effect<void, never, never>) => Effect.Effect<{
|
|
24
|
+
destination: string & import("effect/Brand").Brand<"SshDestination">;
|
|
25
|
+
port: Option.Option<never>;
|
|
26
|
+
hostKey: Option.Option<never>;
|
|
27
|
+
}, MachineUnavailableError, never>;
|
|
28
|
+
tag: (id: string & import("effect/Brand").Brand<"MachineId">, tags: MachineTags) => Effect.Effect<void, ProviderApiError, never>;
|
|
29
|
+
remove: (id: string & import("effect/Brand").Brand<"MachineId">) => Effect.Effect<void, ProviderApiError, never>;
|
|
30
|
+
list: (instance: string, listed: string & import("effect/Brand").Brand<"WorkerName">) => Effect.Effect<ListedMachine[], ProviderApiError, never>;
|
|
31
|
+
}, never, import("effect/http/HttpClient").HttpClient>;
|
|
32
|
+
//# sourceMappingURL=hetzner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hetzner.d.ts","sourceRoot":"","sources":["../../../src/ssh/provisioner/hetzner.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAY,MAAM,EAAE,MAAM,EAAoB,MAAM,QAAQ,CAAC;AACpE,OAAO,EACL,uBAAuB,EAEvB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAQtD,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,WAAW,EAChB,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAEpB,6BAA6B;AAC7B,eAAO,MAAM,WAAW,iCAAiC,CAAC;AAmG1D;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,WACnB,UAAU,YACR,kBAAkB,eACf,OAAO,SACd,MAAM;;;;;;;;;;;;;;;;;sDAqWV,CAAC"}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { MachineId, SshDestination } from "@usine/core/worker";
|
|
2
|
+
import { DateTime, Effect, Option, Redacted, Schema } from "effect";
|
|
3
|
+
import { MachineUnavailableError, WorkerCapabilityError, } from "../../executor.js";
|
|
4
|
+
import { generateHostKey } from "./host-key.js";
|
|
5
|
+
import { failureText, providerApiFor, } from "./http.js";
|
|
6
|
+
import { attemptTag, ProviderApiError, tagValue, } from "./model.js";
|
|
7
|
+
/** The Hetzner Cloud API. */
|
|
8
|
+
export const HETZNER_API = "https://api.hetzner.cloud/v1";
|
|
9
|
+
const LABELS = {
|
|
10
|
+
instance: "usine-instance",
|
|
11
|
+
worker: "usine-worker",
|
|
12
|
+
run: "usine-run",
|
|
13
|
+
process: "usine-process",
|
|
14
|
+
attempt: "usine-attempt",
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Returns the cloud-init user data that replaces the image's SSH host keys with `hostKey` at the
|
|
18
|
+
* server's first boot, so the executor knows the key before it connects. cloud-init runs it on a
|
|
19
|
+
* server created from a snapshot too, because the new server has a new instance ID.
|
|
20
|
+
*/
|
|
21
|
+
const hostKeyUserData = (hostKey) => [
|
|
22
|
+
"#cloud-config",
|
|
23
|
+
"ssh_deletekeys: true",
|
|
24
|
+
"ssh_genkeytypes: []",
|
|
25
|
+
"ssh_keys:",
|
|
26
|
+
" ed25519_private: |",
|
|
27
|
+
...Redacted.value(hostKey.privateKey)
|
|
28
|
+
.trimEnd()
|
|
29
|
+
.split("\n")
|
|
30
|
+
.map((line) => ` ${line}`),
|
|
31
|
+
` ed25519_public: ${hostKey.publicKey}`,
|
|
32
|
+
"",
|
|
33
|
+
].join("\n");
|
|
34
|
+
const Pagination = Schema.Struct({
|
|
35
|
+
meta: Schema.optionalKey(Schema.Struct({
|
|
36
|
+
pagination: Schema.Struct({ next_page: Schema.NullOr(Schema.Int) }),
|
|
37
|
+
})),
|
|
38
|
+
});
|
|
39
|
+
const Named = Schema.Struct({ id: Schema.Int, name: Schema.NullOr(Schema.String) });
|
|
40
|
+
const SshKeys = Schema.Struct({ ssh_keys: Schema.Array(Named) });
|
|
41
|
+
const Networks = Schema.Struct({ networks: Schema.Array(Named) });
|
|
42
|
+
const Firewalls = Schema.Struct({ firewalls: Schema.Array(Named) });
|
|
43
|
+
const ServerTypes = Schema.Struct({
|
|
44
|
+
server_types: Schema.Array(Schema.Struct({
|
|
45
|
+
name: Schema.String,
|
|
46
|
+
locations: Schema.optionalKey(Schema.Array(Schema.Struct({
|
|
47
|
+
name: Schema.String,
|
|
48
|
+
available: Schema.optionalKey(Schema.Boolean),
|
|
49
|
+
}))),
|
|
50
|
+
})),
|
|
51
|
+
});
|
|
52
|
+
const Image = Schema.Struct({
|
|
53
|
+
id: Schema.Int,
|
|
54
|
+
type: Schema.String,
|
|
55
|
+
status: Schema.String,
|
|
56
|
+
description: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
57
|
+
created: Schema.optionalKey(Schema.String),
|
|
58
|
+
});
|
|
59
|
+
const Images = Schema.Struct({ images: Schema.Array(Image) });
|
|
60
|
+
const Server = Schema.Struct({
|
|
61
|
+
id: Schema.Int,
|
|
62
|
+
status: Schema.String,
|
|
63
|
+
created: Schema.String,
|
|
64
|
+
labels: Schema.Record(Schema.String, Schema.String),
|
|
65
|
+
public_net: Schema.optionalKey(Schema.Struct({
|
|
66
|
+
ipv4: Schema.optionalKey(Schema.NullOr(Schema.Struct({ ip: Schema.String }))),
|
|
67
|
+
})),
|
|
68
|
+
private_net: Schema.optionalKey(Schema.Array(Schema.Struct({ network: Schema.Int, ip: Schema.String }))),
|
|
69
|
+
});
|
|
70
|
+
const Created = Schema.Struct({ server: Server });
|
|
71
|
+
const OneServer = Schema.Struct({ server: Server });
|
|
72
|
+
const Servers = Schema.Struct({ servers: Schema.Array(Server) });
|
|
73
|
+
/** Returns the reason a creation failure is retried, by Hetzner's documented error codes. */
|
|
74
|
+
const creationReason = (failure) => {
|
|
75
|
+
if (failure._tag === "ProviderUnanswered")
|
|
76
|
+
return "unreachable";
|
|
77
|
+
const code = Option.getOrElse(failure.code, () => "");
|
|
78
|
+
if (code === "resource_unavailable" || code === "unavailable" || code === "placement_error")
|
|
79
|
+
return "capacity";
|
|
80
|
+
if (code === "resource_limit_exceeded")
|
|
81
|
+
return "quota";
|
|
82
|
+
if (failure.status === 401 || failure.status === 403 || failure.status === 422)
|
|
83
|
+
return "refused";
|
|
84
|
+
return "provider";
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Returns the Hetzner Cloud driver for `worker`'s servers (#550). Each server is created from
|
|
88
|
+
* `settings.image` with the project's SSH key, and labeled with the instance, Worker, Run,
|
|
89
|
+
* creating process, and Attempt. With `privateOnly`, a server gets no public address and is
|
|
90
|
+
* reached at its address on `settings.network`, through the Gateway.
|
|
91
|
+
*/
|
|
92
|
+
export const hetznerDriverFor = (worker, settings, privateOnly, base = HETZNER_API) => Effect.gen(function* () {
|
|
93
|
+
const api = yield* providerApiFor("hetzner", base, settings.token);
|
|
94
|
+
const apiError = (operation) => (failure) => new ProviderApiError({
|
|
95
|
+
provider: "hetzner",
|
|
96
|
+
operation,
|
|
97
|
+
status: failure._tag === "ProviderRefused" ? Option.some(failure.status) : Option.none(),
|
|
98
|
+
message: `Hetzner Cloud couldn't ${operation}: ${failureText(failure)}`,
|
|
99
|
+
});
|
|
100
|
+
const providerGap = (message, cause) => new WorkerCapabilityError({ worker, capability: "provider", message, cause });
|
|
101
|
+
/** Returns every page of a listing at `path`. */
|
|
102
|
+
const listAll = (path, query, schema, operation) => Effect.gen(function* () {
|
|
103
|
+
const pages = [];
|
|
104
|
+
let page = 1;
|
|
105
|
+
while (page !== null) {
|
|
106
|
+
const response = yield* api.send("GET", path, {
|
|
107
|
+
query: { ...query, page: String(page), per_page: "50" },
|
|
108
|
+
});
|
|
109
|
+
pages.push(yield* api.decode(schema, operation)(response));
|
|
110
|
+
const meta = yield* api.decode(Pagination, operation)(response);
|
|
111
|
+
page = meta.meta?.pagination.next_page ?? null;
|
|
112
|
+
}
|
|
113
|
+
return pages;
|
|
114
|
+
});
|
|
115
|
+
/** Returns the ID of `settings.image`: a numeric ID, a system image name, or the newest snapshot with that description. */
|
|
116
|
+
const imageId = Effect.fn("HetznerDriver.imageId")(function* () {
|
|
117
|
+
const image = settings.image;
|
|
118
|
+
if (/^[0-9]+$/u.test(image)) {
|
|
119
|
+
const response = yield* api.send("GET", `/images/${image}`);
|
|
120
|
+
return (yield* api.decode(Schema.Struct({ image: Image }), "read the image")(response))
|
|
121
|
+
.image.id;
|
|
122
|
+
}
|
|
123
|
+
const named = yield* listAll("/images", { name: image }, Images, "list images");
|
|
124
|
+
const system = named.flatMap(({ images }) => images)[0];
|
|
125
|
+
if (system !== undefined)
|
|
126
|
+
return system.id;
|
|
127
|
+
const snapshots = (yield* listAll("/images", { type: "snapshot", status: "available" }, Images, "list snapshots"))
|
|
128
|
+
.flatMap(({ images }) => images)
|
|
129
|
+
.filter(({ description }) => description === image)
|
|
130
|
+
.sort((left, right) => (right.created ?? "").localeCompare(left.created ?? ""));
|
|
131
|
+
const newest = snapshots[0];
|
|
132
|
+
if (newest === undefined)
|
|
133
|
+
return yield* providerGap(`Hetzner Cloud has no snapshot or image "${image}" for worker "${worker}". Build one with infra/hetzner/snapshot.sh, or set provisioner.image to an existing one.`, new Error(`no image ${image}`));
|
|
134
|
+
return newest.id;
|
|
135
|
+
});
|
|
136
|
+
/** Returns the ID of the project's SSH key, network, or firewall named `name`. */
|
|
137
|
+
const named = Effect.fn("HetznerDriver.named")(function* (resource, name) {
|
|
138
|
+
const response = yield* api.send("GET", `/${resource}`, { query: { name } });
|
|
139
|
+
const operation = `list ${resource}`;
|
|
140
|
+
const listed = resource === "ssh_keys"
|
|
141
|
+
? (yield* api.decode(SshKeys, operation)(response)).ssh_keys
|
|
142
|
+
: resource === "networks"
|
|
143
|
+
? (yield* api.decode(Networks, operation)(response)).networks
|
|
144
|
+
: (yield* api.decode(Firewalls, operation)(response)).firewalls;
|
|
145
|
+
return Option.fromNullishOr(listed[0]?.id);
|
|
146
|
+
});
|
|
147
|
+
const labelsOf = (tags) => ({
|
|
148
|
+
...settings.labels,
|
|
149
|
+
[LABELS.instance]: tagValue(tags.instance),
|
|
150
|
+
[LABELS.worker]: tagValue(tags.worker),
|
|
151
|
+
[LABELS.run]: tagValue(tags.run),
|
|
152
|
+
[LABELS.process]: tagValue(tags.process),
|
|
153
|
+
...Option.match(tags.attempt, {
|
|
154
|
+
onNone: () => ({}),
|
|
155
|
+
onSome: (attempt) => ({ [LABELS.attempt]: attemptTag(attempt) }),
|
|
156
|
+
}),
|
|
157
|
+
});
|
|
158
|
+
const verify = Effect.fn("HetznerDriver.verify")(function* (_worker, maxConcurrentAttempts) {
|
|
159
|
+
yield* Effect.annotateCurrentSpan({ worker, maxConcurrentAttempts });
|
|
160
|
+
const types = yield* api
|
|
161
|
+
.send("GET", "/server_types", { query: { name: settings.serverType } })
|
|
162
|
+
.pipe(Effect.flatMap(api.decode(ServerTypes, "list server types")));
|
|
163
|
+
const serverType = types.server_types[0];
|
|
164
|
+
if (serverType === undefined)
|
|
165
|
+
return yield* providerGap(`Hetzner Cloud has no server type "${settings.serverType}" for worker "${worker}"`, new Error(`no server type ${settings.serverType}`));
|
|
166
|
+
const location = serverType.locations?.find(({ name }) => name === settings.location);
|
|
167
|
+
if (serverType.locations !== undefined && location === undefined)
|
|
168
|
+
return yield* providerGap(`Hetzner Cloud doesn't offer server type ${settings.serverType} in ${settings.location} for worker "${worker}"`, new Error(`no location ${settings.location} for ${settings.serverType}`));
|
|
169
|
+
const image = yield* imageId();
|
|
170
|
+
const key = yield* named("ssh_keys", settings.sshKey);
|
|
171
|
+
if (Option.isNone(key))
|
|
172
|
+
return yield* providerGap(`The Hetzner Cloud project of worker "${worker}" has no SSH key named "${settings.sshKey}". Add the public key of identity_file with hcloud ssh-key create.`, new Error(`no SSH key ${settings.sshKey}`));
|
|
173
|
+
if (Option.isSome(settings.network)) {
|
|
174
|
+
const network = yield* named("networks", settings.network.value);
|
|
175
|
+
if (Option.isNone(network))
|
|
176
|
+
return yield* providerGap(`The Hetzner Cloud project of worker "${worker}" has no network named "${settings.network.value}"`, new Error(`no network ${settings.network.value}`));
|
|
177
|
+
}
|
|
178
|
+
if (Option.isSome(settings.firewall)) {
|
|
179
|
+
const firewall = yield* named("firewalls", settings.firewall.value);
|
|
180
|
+
if (Option.isNone(firewall))
|
|
181
|
+
return yield* providerGap(`The Hetzner Cloud project of worker "${worker}" has no firewall named "${settings.firewall.value}"`, new Error(`no firewall ${settings.firewall.value}`));
|
|
182
|
+
}
|
|
183
|
+
const warnings = location?.available === false
|
|
184
|
+
? [
|
|
185
|
+
`Hetzner Cloud has no ${settings.serverType} available in ${settings.location} now, so machines can't be created until it has. Each attempt waits and tries again.`,
|
|
186
|
+
]
|
|
187
|
+
: [];
|
|
188
|
+
yield* Effect.logInfo("Hetzner Cloud provisioner verified", {
|
|
189
|
+
worker,
|
|
190
|
+
serverType: settings.serverType,
|
|
191
|
+
location: settings.location,
|
|
192
|
+
available: location?.available ?? "not reported",
|
|
193
|
+
image,
|
|
194
|
+
network: Option.getOrElse(settings.network, () => "none"),
|
|
195
|
+
// The API documents no endpoint for the project's server limit.
|
|
196
|
+
limits: "not documented",
|
|
197
|
+
});
|
|
198
|
+
return { warnings };
|
|
199
|
+
}, (effect) => effect.pipe(Effect.catchTags({
|
|
200
|
+
ProviderUnanswered: (failure) => Effect.fail(providerGap(`Can't reach the Hetzner Cloud API for worker "${worker}": ${failure.detail}`, failure)),
|
|
201
|
+
ProviderRefused: (failure) => Effect.fail(providerGap(failure.status === 401
|
|
202
|
+
? `Hetzner Cloud refused the token of worker "${worker}". Create a read and write API token for the project, and set it in provisioner.token_env.`
|
|
203
|
+
: `Hetzner Cloud refused a preflight request of worker "${worker}": ${failureText(failure)}`, failure)),
|
|
204
|
+
})));
|
|
205
|
+
const create = Effect.fn("HetznerDriver.create")(function* (name, tags) {
|
|
206
|
+
yield* Effect.annotateCurrentSpan({ worker, name, run: tags.run });
|
|
207
|
+
const image = yield* imageId().pipe(Effect.catchTag("WorkerCapabilityError", (gap) => Effect.fail(new MachineUnavailableError({
|
|
208
|
+
worker,
|
|
209
|
+
provider: "hetzner",
|
|
210
|
+
reason: "refused",
|
|
211
|
+
message: gap.message,
|
|
212
|
+
}))));
|
|
213
|
+
const key = yield* named("ssh_keys", settings.sshKey);
|
|
214
|
+
const network = Option.isSome(settings.network)
|
|
215
|
+
? yield* named("networks", settings.network.value)
|
|
216
|
+
: Option.none();
|
|
217
|
+
const firewall = Option.isSome(settings.firewall)
|
|
218
|
+
? yield* named("firewalls", settings.firewall.value)
|
|
219
|
+
: Option.none();
|
|
220
|
+
const hostKey = yield* generateHostKey;
|
|
221
|
+
const response = yield* api.send("POST", "/servers", {
|
|
222
|
+
body: {
|
|
223
|
+
name,
|
|
224
|
+
server_type: settings.serverType,
|
|
225
|
+
location: settings.location,
|
|
226
|
+
image: String(image),
|
|
227
|
+
ssh_keys: Option.match(key, {
|
|
228
|
+
onNone: () => [settings.sshKey],
|
|
229
|
+
onSome: (id) => [String(id)],
|
|
230
|
+
}),
|
|
231
|
+
networks: Option.match(network, { onNone: () => [], onSome: (id) => [id] }),
|
|
232
|
+
firewalls: Option.match(firewall, {
|
|
233
|
+
onNone: () => [],
|
|
234
|
+
onSome: (id) => [{ firewall: id }],
|
|
235
|
+
}),
|
|
236
|
+
public_net: { enable_ipv4: !privateOnly, enable_ipv6: false },
|
|
237
|
+
labels: labelsOf(tags),
|
|
238
|
+
user_data: hostKeyUserData(hostKey),
|
|
239
|
+
start_after_create: true,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
const created = yield* api.decode(Created, "create a server")(response);
|
|
243
|
+
return {
|
|
244
|
+
id: MachineId.make(String(created.server.id)),
|
|
245
|
+
hostKey: Option.some(hostKey.publicKey),
|
|
246
|
+
};
|
|
247
|
+
}, (effect, _name, _tags) => effect.pipe(Effect.catchTags({
|
|
248
|
+
ProviderUnanswered: (failure) => Effect.fail(new MachineUnavailableError({
|
|
249
|
+
worker,
|
|
250
|
+
provider: "hetzner",
|
|
251
|
+
reason: "unreachable",
|
|
252
|
+
message: `Can't reach the Hetzner Cloud API to create a server: ${failure.detail}`,
|
|
253
|
+
})),
|
|
254
|
+
ProviderRefused: (failure) => Effect.fail(new MachineUnavailableError({
|
|
255
|
+
worker,
|
|
256
|
+
provider: "hetzner",
|
|
257
|
+
reason: creationReason(failure),
|
|
258
|
+
message: `Hetzner Cloud didn't create a ${settings.serverType} server in ${settings.location}: ${failureText(failure)}`,
|
|
259
|
+
})),
|
|
260
|
+
})));
|
|
261
|
+
/** Returns the address to connect to: the private one behind a Gateway, else the public IPv4. */
|
|
262
|
+
const addressOf = (server) => Option.fromNullishOr(privateOnly ? server.private_net?.[0]?.ip : server.public_net?.ipv4?.ip);
|
|
263
|
+
const endpoint = Effect.fn("HetznerDriver.endpoint")(function* (id, interval) {
|
|
264
|
+
yield* Effect.annotateCurrentSpan({ worker, machine: id });
|
|
265
|
+
while (true) {
|
|
266
|
+
const server = (yield* api
|
|
267
|
+
.send("GET", `/servers/${id}`)
|
|
268
|
+
.pipe(Effect.flatMap(api.decode(OneServer, "read the server")))).server;
|
|
269
|
+
const address = addressOf(server);
|
|
270
|
+
if (server.status === "running" && Option.isSome(address))
|
|
271
|
+
return {
|
|
272
|
+
destination: SshDestination.make(`${settings.sshUser}@${address.value}`),
|
|
273
|
+
port: Option.none(),
|
|
274
|
+
hostKey: Option.none(),
|
|
275
|
+
};
|
|
276
|
+
yield* Effect.logDebug("Waiting for the Hetzner Cloud server to run", {
|
|
277
|
+
worker,
|
|
278
|
+
machine: id,
|
|
279
|
+
status: server.status,
|
|
280
|
+
});
|
|
281
|
+
yield* interval;
|
|
282
|
+
}
|
|
283
|
+
}, (effect) => effect.pipe(Effect.catchTags({
|
|
284
|
+
ProviderUnanswered: (failure) => Effect.fail(new MachineUnavailableError({
|
|
285
|
+
worker,
|
|
286
|
+
provider: "hetzner",
|
|
287
|
+
reason: "unreachable",
|
|
288
|
+
message: `Can't reach the Hetzner Cloud API to read a new server: ${failure.detail}`,
|
|
289
|
+
})),
|
|
290
|
+
ProviderRefused: (failure) => Effect.fail(new MachineUnavailableError({
|
|
291
|
+
worker,
|
|
292
|
+
provider: "hetzner",
|
|
293
|
+
reason: failure.status === 404 ? "capacity" : creationReason(failure),
|
|
294
|
+
message: failure.status === 404
|
|
295
|
+
? "Hetzner Cloud removed the new server before it ran"
|
|
296
|
+
: `Hetzner Cloud couldn't read the new server: ${failureText(failure)}`,
|
|
297
|
+
})),
|
|
298
|
+
})));
|
|
299
|
+
const tag = Effect.fn("HetznerDriver.tag")(function* (id, tags) {
|
|
300
|
+
yield* Effect.annotateCurrentSpan({ worker, machine: id });
|
|
301
|
+
yield* api
|
|
302
|
+
.send("PUT", `/servers/${id}`, { body: { labels: labelsOf(tags) } })
|
|
303
|
+
.pipe(Effect.mapError(apiError("label the server")));
|
|
304
|
+
});
|
|
305
|
+
const remove = Effect.fn("HetznerDriver.remove")(function* (id) {
|
|
306
|
+
yield* Effect.annotateCurrentSpan({ worker, machine: id });
|
|
307
|
+
yield* api.send("DELETE", `/servers/${id}`).pipe(Effect.asVoid, Effect.catchIf((failure) => failure._tag === "ProviderRefused" && failure.status === 404, () => Effect.void), Effect.mapError(apiError("delete the server")));
|
|
308
|
+
});
|
|
309
|
+
const list = Effect.fn("HetznerDriver.list")(function* (instance, listed) {
|
|
310
|
+
yield* Effect.annotateCurrentSpan({ worker: listed });
|
|
311
|
+
const pages = yield* listAll("/servers", {
|
|
312
|
+
label_selector: `${LABELS.instance}=${tagValue(instance)},${LABELS.worker}=${tagValue(listed)}`,
|
|
313
|
+
}, Servers, "list servers").pipe(Effect.mapError(apiError("list the servers")));
|
|
314
|
+
return pages
|
|
315
|
+
.flatMap(({ servers }) => servers)
|
|
316
|
+
.map((server) => ({
|
|
317
|
+
id: MachineId.make(String(server.id)),
|
|
318
|
+
tags: {
|
|
319
|
+
worker: server.labels[LABELS.worker] ?? "",
|
|
320
|
+
run: server.labels[LABELS.run] ?? "",
|
|
321
|
+
process: server.labels[LABELS.process] ?? "",
|
|
322
|
+
attempt: Option.fromNullishOr(server.labels[LABELS.attempt]),
|
|
323
|
+
},
|
|
324
|
+
createdAt: Option.getOrElse(DateTime.make(server.created), () => DateTime.makeUnsafe(0)),
|
|
325
|
+
}));
|
|
326
|
+
});
|
|
327
|
+
return {
|
|
328
|
+
provider: "hetzner",
|
|
329
|
+
verify,
|
|
330
|
+
create,
|
|
331
|
+
endpoint,
|
|
332
|
+
tag,
|
|
333
|
+
remove,
|
|
334
|
+
list,
|
|
335
|
+
};
|
|
336
|
+
});
|
|
337
|
+
//# sourceMappingURL=hetzner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hetzner.js","sourceRoot":"","sources":["../../../src/ssh/provisioner/hetzner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Effect, Redacted } from "effect";
|
|
2
|
+
/** A machine's SSH host key pair, which the executor generates before it creates the machine. */
|
|
3
|
+
export interface HostKeyPair {
|
|
4
|
+
/** The public key as `known_hosts` writes it: `ssh-ed25519 <base64>`. */
|
|
5
|
+
readonly publicKey: string;
|
|
6
|
+
/** The private key as an unencrypted OpenSSH key file, which sshd reads. */
|
|
7
|
+
readonly privateKey: Redacted.Redacted<string>;
|
|
8
|
+
}
|
|
9
|
+
/** Generates an Ed25519 host key pair for a new machine. */
|
|
10
|
+
export declare const generateHostKey: Effect.Effect<HostKeyPair>;
|
|
11
|
+
//# sourceMappingURL=host-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-key.d.ts","sourceRoot":"","sources":["../../../src/ssh/provisioner/host-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAG1C,iGAAiG;AACjG,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CAChD;AAoED,4DAA4D;AAC5D,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,WAAW,CAqBrD,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Effect, Redacted } from "effect";
|
|
2
|
+
import { Base64 } from "effect/encoding";
|
|
3
|
+
const KEY_TYPE = "ssh-ed25519";
|
|
4
|
+
/** The length of the PKCS #8 header of an Ed25519 private key, which the 32-byte seed follows. */
|
|
5
|
+
const PKCS8_HEADER_LENGTH = 16;
|
|
6
|
+
const encoder = new TextEncoder();
|
|
7
|
+
const concat = (...parts) => {
|
|
8
|
+
const joined = new Uint8Array(parts.reduce((length, part) => length + part.length, 0));
|
|
9
|
+
let offset = 0;
|
|
10
|
+
for (const part of parts) {
|
|
11
|
+
joined.set(part, offset);
|
|
12
|
+
offset += part.length;
|
|
13
|
+
}
|
|
14
|
+
return joined;
|
|
15
|
+
};
|
|
16
|
+
const uint32 = (value) => {
|
|
17
|
+
const bytes = new Uint8Array(4);
|
|
18
|
+
new DataView(bytes.buffer).setUint32(0, value);
|
|
19
|
+
return bytes;
|
|
20
|
+
};
|
|
21
|
+
/** The SSH wire format's `string`: a 32-bit length, then the bytes. */
|
|
22
|
+
const sshBytes = (bytes) => concat(uint32(bytes.length), bytes);
|
|
23
|
+
/** The SSH wire format's `string` of `text`'s UTF-8 bytes. */
|
|
24
|
+
const sshText = (text) => sshBytes(encoder.encode(text));
|
|
25
|
+
/** The magic that starts an OpenSSH private key: `openssh-key-v1` and a zero byte. */
|
|
26
|
+
const MAGIC = concat(encoder.encode("openssh-key-v1"), new Uint8Array(1));
|
|
27
|
+
/**
|
|
28
|
+
* Returns the unencrypted OpenSSH key file of an Ed25519 key, in the format of `PROTOCOL.key`
|
|
29
|
+
* in OpenSSH's sources. `check` is the random 32-bit value that the format writes twice.
|
|
30
|
+
*/
|
|
31
|
+
const openSshKeyFile = (seed, publicKey, check) => {
|
|
32
|
+
const unpadded = concat(check, check, sshText(KEY_TYPE), sshBytes(publicKey), sshBytes(concat(seed, publicKey)), sshText(""));
|
|
33
|
+
const padding = Uint8Array.from({ length: (8 - (unpadded.length % 8)) % 8 }, (_, index) => index + 1);
|
|
34
|
+
const body = concat(MAGIC, sshText("none"), sshText("none"), sshText(""), uint32(1), sshBytes(concat(sshText(KEY_TYPE), sshBytes(publicKey))), sshBytes(concat(unpadded, padding)));
|
|
35
|
+
return [
|
|
36
|
+
"-----BEGIN OPENSSH PRIVATE KEY-----",
|
|
37
|
+
...(Base64.encode(body).match(/.{1,70}/gu) ?? []),
|
|
38
|
+
"-----END OPENSSH PRIVATE KEY-----",
|
|
39
|
+
"",
|
|
40
|
+
].join("\n");
|
|
41
|
+
};
|
|
42
|
+
/** Generates an Ed25519 host key pair for a new machine. */
|
|
43
|
+
export const generateHostKey = Effect.gen(function* () {
|
|
44
|
+
const pair = yield* Effect.promise(() => crypto.subtle.generateKey({ name: "Ed25519" }, true, ["sign", "verify"]));
|
|
45
|
+
if (!("privateKey" in pair))
|
|
46
|
+
return yield* Effect.die(new Error("WebCrypto returned one Ed25519 key, not a pair"));
|
|
47
|
+
const publicKey = new Uint8Array(yield* Effect.promise(() => crypto.subtle.exportKey("raw", pair.publicKey)));
|
|
48
|
+
const pkcs8 = new Uint8Array(yield* Effect.promise(() => crypto.subtle.exportKey("pkcs8", pair.privateKey)));
|
|
49
|
+
const seed = pkcs8.slice(PKCS8_HEADER_LENGTH);
|
|
50
|
+
if (publicKey.length !== 32 || seed.length !== 32)
|
|
51
|
+
return yield* Effect.die(new Error("WebCrypto exported an Ed25519 key of an unexpected size"));
|
|
52
|
+
return {
|
|
53
|
+
publicKey: `${KEY_TYPE} ${Base64.encode(concat(sshText(KEY_TYPE), sshBytes(publicKey)))}`,
|
|
54
|
+
privateKey: Redacted.make(openSshKeyFile(seed, publicKey, crypto.getRandomValues(new Uint8Array(4)))),
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=host-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-key.js","sourceRoot":"","sources":["../../../src/ssh/provisioner/host-key.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { MachineProvider } from "@usine/core/worker";
|
|
2
|
+
import { Effect, Option, type Redacted, Schema } from "effect";
|
|
3
|
+
import { HttpClient } from "effect/http";
|
|
4
|
+
declare const ProviderUnanswered_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
5
|
+
readonly _tag: "ProviderUnanswered";
|
|
6
|
+
} & Readonly<A>;
|
|
7
|
+
/** A provider API request that got no response or an unreadable one. */
|
|
8
|
+
export declare class ProviderUnanswered extends ProviderUnanswered_base<{
|
|
9
|
+
readonly detail: string;
|
|
10
|
+
}> {
|
|
11
|
+
}
|
|
12
|
+
declare const ProviderRefused_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
13
|
+
readonly _tag: "ProviderRefused";
|
|
14
|
+
} & Readonly<A>;
|
|
15
|
+
/** A provider API response with an error status, with the provider's error code and message. */
|
|
16
|
+
declare class ProviderRefused extends ProviderRefused_base<{
|
|
17
|
+
readonly status: number;
|
|
18
|
+
readonly code: Option.Option<string>;
|
|
19
|
+
readonly message: string;
|
|
20
|
+
}> {
|
|
21
|
+
}
|
|
22
|
+
export type ProviderFailure = ProviderUnanswered | ProviderRefused;
|
|
23
|
+
/** A decoded provider API response. */
|
|
24
|
+
export interface ProviderResponse {
|
|
25
|
+
readonly status: number;
|
|
26
|
+
/** The JSON body, or `null` if the response has none. */
|
|
27
|
+
readonly body: Schema.Json;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Returns a client for a provider's JSON API at `base` with `token` as its bearer token. Each
|
|
31
|
+
* request has a timeout, and an error status fails with the provider's code and message. The
|
|
32
|
+
* token and request body never appear in a failure.
|
|
33
|
+
*/
|
|
34
|
+
export declare const providerApiFor: (provider: MachineProvider, base: string, token: Redacted.Redacted<string>) => Effect.Effect<{
|
|
35
|
+
readonly send: (method: "DELETE" | "GET" | "PATCH" | "POST" | "PUT", path: string, options?: {
|
|
36
|
+
readonly body?: unknown;
|
|
37
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
38
|
+
readonly query?: Readonly<Record<string, string>>;
|
|
39
|
+
} | undefined) => Effect.Effect<ProviderResponse, ProviderRefused | ProviderUnanswered, never>;
|
|
40
|
+
readonly decode: <S extends Schema.Constraint>(schema: S, operation: string) => (response: ProviderResponse) => Effect.Effect<S["Type"], ProviderUnanswered, S["DecodingServices"]>;
|
|
41
|
+
}, never, HttpClient.HttpClient>;
|
|
42
|
+
/** Returns a failure as an operator-facing clause. */
|
|
43
|
+
export declare const failureText: (failure: ProviderFailure) => string;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/ssh/provisioner/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAQ,MAAM,EAAE,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,UAAU,EAAsC,MAAM,aAAa,CAAC;;;;AAK7E,wEAAwE;AACxE,qBAAa,kBAAmB,SAAQ,wBAAuC;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;CAAG;;;;AAEL,gGAAgG;AAChG,cAAM,eAAgB,SAAQ,qBAAoC;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;AAEL,MAAM,MAAM,eAAe,GAAG,kBAAkB,GAAG,eAAe,CAAC;AAEnE,uCAAuC;AACvC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;CAC5B;AAmCD;;;;GAIG;AACH,eAAO,MAAM,cAAc,aACf,eAAe,QACnB,MAAM,SACL,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;;wBAQV,OAAO;2BACJ,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;yBAClC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;sBAiDlD,CAAC,SAAS,MAAM,CAAC,UAAU,UAAU,CAAC,aAAa,MAAM,gBAE9C,gBAAgB,KACzB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC,CAAC,kBAAkB,CAAC,CAAC;gCAWxE,CAAC;AAEL,sDAAsD;AACtD,eAAO,MAAM,WAAW,YAAa,eAAe,KAAG,MAMzB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Data, Effect, Option, Schema } from "effect";
|
|
2
|
+
import { HttpClient, HttpClientError, HttpClientRequest } from "effect/http";
|
|
3
|
+
/** The timeout for one provider API request. */
|
|
4
|
+
const REQUEST_TIMEOUT = "30 seconds";
|
|
5
|
+
/** A provider API request that got no response or an unreadable one. */
|
|
6
|
+
export class ProviderUnanswered extends Data.TaggedError("ProviderUnanswered") {
|
|
7
|
+
}
|
|
8
|
+
/** A provider API response with an error status, with the provider's error code and message. */
|
|
9
|
+
class ProviderRefused extends Data.TaggedError("ProviderRefused") {
|
|
10
|
+
}
|
|
11
|
+
const ErrorFields = Schema.Struct({
|
|
12
|
+
code: Schema.optionalKey(Schema.String),
|
|
13
|
+
message: Schema.optionalKey(Schema.String),
|
|
14
|
+
});
|
|
15
|
+
/** Hetzner's error body, `{ error: { code, message } }`. */
|
|
16
|
+
const NestedErrorBody = Schema.Struct({ error: ErrorFields });
|
|
17
|
+
const decodeNestedError = Schema.decodeUnknownOption(NestedErrorBody);
|
|
18
|
+
const decodeFlatError = Schema.decodeUnknownOption(ErrorFields);
|
|
19
|
+
const decodeJson = Schema.decodeEffect(Schema.fromJsonString(Schema.Json));
|
|
20
|
+
/** Returns the code and message of Hetzner's or Boat's error body (Boat's are top-level). */
|
|
21
|
+
const errorOf = (response) => {
|
|
22
|
+
const fields = Option.orElse(Option.map(decodeNestedError(response.body), ({ error }) => error), () => decodeFlatError(response.body));
|
|
23
|
+
return {
|
|
24
|
+
code: Option.flatMap(fields, ({ code }) => Option.fromNullishOr(code)),
|
|
25
|
+
message: Option.getOrElse(Option.flatMap(fields, ({ message }) => Option.fromNullishOr(message)), () => "no message"),
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Returns a client for a provider's JSON API at `base` with `token` as its bearer token. Each
|
|
30
|
+
* request has a timeout, and an error status fails with the provider's code and message. The
|
|
31
|
+
* token and request body never appear in a failure.
|
|
32
|
+
*/
|
|
33
|
+
export const providerApiFor = (provider, base, token) => Effect.gen(function* () {
|
|
34
|
+
const http = yield* HttpClient.HttpClient;
|
|
35
|
+
const send = Effect.fn("ProviderApi.send")(function* (method, path, options = {}) {
|
|
36
|
+
yield* Effect.annotateCurrentSpan({ provider, method, path });
|
|
37
|
+
const url = new URL(`${base}${path}`);
|
|
38
|
+
for (const [key, value] of Object.entries(options.query ?? {}))
|
|
39
|
+
url.searchParams.set(key, value);
|
|
40
|
+
const request = HttpClientRequest.make(method)(url).pipe(HttpClientRequest.bearerToken(token), HttpClientRequest.acceptJson, HttpClientRequest.setHeaders(options.headers ?? {}), options.body === undefined
|
|
41
|
+
? (unchanged) => unchanged
|
|
42
|
+
: HttpClientRequest.bodyJsonUnsafe(options.body));
|
|
43
|
+
const response = yield* http.execute(request).pipe(Effect.timeout(REQUEST_TIMEOUT), Effect.mapError((cause) => HttpClientError.isHttpClientError(cause)
|
|
44
|
+
? new ProviderUnanswered({ detail: `no response (${cause.reason._tag})` })
|
|
45
|
+
: new ProviderUnanswered({ detail: `no response within ${REQUEST_TIMEOUT}` })));
|
|
46
|
+
const text = yield* response.text.pipe(Effect.mapError(() => new ProviderUnanswered({ detail: "the response body can't be read" })));
|
|
47
|
+
const body = text === ""
|
|
48
|
+
? null
|
|
49
|
+
: yield* decodeJson(text).pipe(Effect.mapError(() => new ProviderUnanswered({
|
|
50
|
+
detail: `the response with status ${String(response.status)} isn't JSON`,
|
|
51
|
+
})));
|
|
52
|
+
const answer = { status: response.status, body };
|
|
53
|
+
if (response.status >= 400) {
|
|
54
|
+
const { code, message } = errorOf(answer);
|
|
55
|
+
return yield* new ProviderRefused({ status: response.status, code, message });
|
|
56
|
+
}
|
|
57
|
+
return answer;
|
|
58
|
+
});
|
|
59
|
+
/** Decodes a response body with `schema`; an unexpected shape counts as unanswered. */
|
|
60
|
+
const decode = (schema, operation) => (response) => Schema.decodeUnknownEffect(schema)(response.body).pipe(Effect.mapError(() => new ProviderUnanswered({
|
|
61
|
+
detail: `the response to ${operation} doesn't have the documented shape`,
|
|
62
|
+
})));
|
|
63
|
+
return { send, decode };
|
|
64
|
+
});
|
|
65
|
+
/** Returns a failure as an operator-facing clause. */
|
|
66
|
+
export const failureText = (failure) => failure._tag === "ProviderUnanswered"
|
|
67
|
+
? failure.detail
|
|
68
|
+
: `status ${String(failure.status)}${Option.match(failure.code, {
|
|
69
|
+
onNone: () => "",
|
|
70
|
+
onSome: (code) => ` ${code}`,
|
|
71
|
+
})}: ${failure.message}`;
|
|
72
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/ssh/provisioner/http.ts"],"names":[],"mappings":"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"}
|