@caelo-cms/provisioning 0.10.47 → 0.10.49
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/caddy/Caddyfile.production +1 -1
- package/caddy/Caddyfile.staging +1 -1
- package/dist/agent/apply.d.ts +45 -0
- package/dist/agent/apply.d.ts.map +1 -0
- package/dist/agent/apply.js +83 -0
- package/dist/agent/apply.js.map +1 -0
- package/dist/agent/cli-provenance.d.ts +42 -0
- package/dist/agent/cli-provenance.d.ts.map +1 -0
- package/dist/agent/cli-provenance.js +138 -0
- package/dist/agent/cli-provenance.js.map +1 -0
- package/dist/agent/command.d.ts +79 -0
- package/dist/agent/command.d.ts.map +1 -0
- package/dist/agent/command.js +454 -0
- package/dist/agent/command.js.map +1 -0
- package/dist/agent/config.d.ts +30 -0
- package/dist/agent/config.d.ts.map +1 -0
- package/dist/agent/config.js +60 -0
- package/dist/agent/config.js.map +1 -0
- package/dist/agent/lock.d.ts +13 -0
- package/dist/agent/lock.d.ts.map +1 -0
- package/dist/agent/lock.js +100 -0
- package/dist/agent/lock.js.map +1 -0
- package/dist/agent/queue.d.ts +35 -0
- package/dist/agent/queue.d.ts.map +1 -0
- package/dist/agent/queue.js +50 -0
- package/dist/agent/queue.js.map +1 -0
- package/dist/agent/run.d.ts +97 -0
- package/dist/agent/run.d.ts.map +1 -0
- package/dist/agent/run.js +198 -0
- package/dist/agent/run.js.map +1 -0
- package/dist/agent/systemd.d.ts +55 -0
- package/dist/agent/systemd.d.ts.map +1 -0
- package/dist/agent/systemd.js +171 -0
- package/dist/agent/systemd.js.map +1 -0
- package/dist/agent/units.d.ts +44 -0
- package/dist/agent/units.d.ts.map +1 -0
- package/dist/agent/units.js +127 -0
- package/dist/agent/units.js.map +1 -0
- package/dist/agent-db.js +18402 -0
- package/dist/cli.js +119 -16
- package/dist/cli.js.map +1 -1
- package/dist/cloudsql-iam-user.d.ts +39 -0
- package/dist/cloudsql-iam-user.d.ts.map +1 -0
- package/dist/cloudsql-iam-user.js +119 -0
- package/dist/cloudsql-iam-user.js.map +1 -0
- package/dist/compose.d.ts +16 -0
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +26 -1
- package/dist/compose.js.map +1 -1
- package/dist/dns/index.d.ts +2 -0
- package/dist/dns/index.d.ts.map +1 -1
- package/dist/dns/index.js +3 -2
- package/dist/dns/index.js.map +1 -1
- package/dist/dns/manual.d.ts +5 -0
- package/dist/dns/manual.d.ts.map +1 -1
- package/dist/dns/manual.js +28 -12
- package/dist/dns/manual.js.map +1 -1
- package/dist/gcloud.d.ts +2 -0
- package/dist/gcloud.d.ts.map +1 -1
- package/dist/gcloud.js +1 -1
- package/dist/gcloud.js.map +1 -1
- package/dist/gcp-names.d.ts +15 -0
- package/dist/gcp-names.d.ts.map +1 -1
- package/dist/gcp-names.js +23 -0
- package/dist/gcp-names.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/infra-contract.d.ts +1 -1
- package/dist/infra-contract.js +7 -1
- package/dist/infra-contract.js.map +1 -1
- package/dist/install-job-runner.d.ts +13 -0
- package/dist/install-job-runner.d.ts.map +1 -0
- package/dist/install-job-runner.js +155 -0
- package/dist/install-job-runner.js.map +1 -0
- package/dist/install-job.d.ts +210 -0
- package/dist/install-job.d.ts.map +1 -0
- package/dist/install-job.js +449 -0
- package/dist/install-job.js.map +1 -0
- package/dist/install-state.d.ts +10 -1
- package/dist/install-state.d.ts.map +1 -1
- package/dist/install-state.js +10 -2
- package/dist/install-state.js.map +1 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +19 -4
- package/dist/lifecycle.js.map +1 -1
- package/dist/migration-runner.d.ts +5 -0
- package/dist/migration-runner.d.ts.map +1 -1
- package/dist/migration-runner.js +1 -1
- package/dist/migration-runner.js.map +1 -1
- package/dist/operator-access.d.ts +14 -8
- package/dist/operator-access.d.ts.map +1 -1
- package/dist/operator-access.js +16 -102
- package/dist/operator-access.js.map +1 -1
- package/dist/release-manifest.d.ts +2 -41
- package/dist/release-manifest.d.ts.map +1 -1
- package/dist/release-manifest.js +18 -121
- package/dist/release-manifest.js.map +1 -1
- package/dist/run-mode.d.ts +27 -0
- package/dist/run-mode.d.ts.map +1 -0
- package/dist/run-mode.js +22 -0
- package/dist/run-mode.js.map +1 -0
- package/dist/runtime-secrets.d.ts +19 -0
- package/dist/runtime-secrets.d.ts.map +1 -1
- package/dist/runtime-secrets.js +44 -0
- package/dist/runtime-secrets.js.map +1 -1
- package/dist/self-hosted-roles.d.ts +22 -1
- package/dist/self-hosted-roles.d.ts.map +1 -1
- package/dist/self-hosted-roles.js +81 -12
- package/dist/self-hosted-roles.js.map +1 -1
- package/dist/self-hosted-upgrade.d.ts +17 -0
- package/dist/self-hosted-upgrade.d.ts.map +1 -1
- package/dist/self-hosted-upgrade.js +16 -1
- package/dist/self-hosted-upgrade.js.map +1 -1
- package/dist/stack-contract.d.ts +6 -0
- package/dist/stack-contract.d.ts.map +1 -1
- package/dist/stack-contract.js +10 -1
- package/dist/stack-contract.js.map +1 -1
- package/dist/upgrade/converge.d.ts +6 -0
- package/dist/upgrade/converge.d.ts.map +1 -1
- package/dist/upgrade/converge.js +59 -2
- package/dist/upgrade/converge.js.map +1 -1
- package/dist/upgrade/gcp-lookup.d.ts +6 -0
- package/dist/upgrade/gcp-lookup.d.ts.map +1 -1
- package/dist/upgrade/gcp-lookup.js +25 -0
- package/dist/upgrade/gcp-lookup.js.map +1 -1
- package/dist/upgrade/release-images.d.ts +26 -2
- package/dist/upgrade/release-images.d.ts.map +1 -1
- package/dist/upgrade/release-images.js +35 -2
- package/dist/upgrade/release-images.js.map +1 -1
- package/dist/upgrade/upgrade.d.ts +5 -0
- package/dist/upgrade/upgrade.d.ts.map +1 -1
- package/dist/upgrade/upgrade.js +28 -1
- package/dist/upgrade/upgrade.js.map +1 -1
- package/dist/upgrader-assess.d.ts +85 -0
- package/dist/upgrader-assess.d.ts.map +1 -0
- package/dist/upgrader-assess.js +122 -0
- package/dist/upgrader-assess.js.map +1 -0
- package/dist/upgrader-gcp.d.ts +17 -0
- package/dist/upgrader-gcp.d.ts.map +1 -0
- package/dist/upgrader-gcp.js +87 -0
- package/dist/upgrader-gcp.js.map +1 -0
- package/dist/upgrader-job.d.ts +133 -0
- package/dist/upgrader-job.d.ts.map +1 -0
- package/dist/upgrader-job.js +259 -0
- package/dist/upgrader-job.js.map +1 -0
- package/dist/upgrader-reporter.d.ts +35 -0
- package/dist/upgrader-reporter.d.ts.map +1 -0
- package/dist/upgrader-reporter.js +79 -0
- package/dist/upgrader-reporter.js.map +1 -0
- package/dist/upgrader-setup.d.ts +59 -0
- package/dist/upgrader-setup.d.ts.map +1 -0
- package/dist/upgrader-setup.js +308 -0
- package/dist/upgrader-setup.js.map +1 -0
- package/dist/wizard.d.ts +6 -0
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +32 -4
- package/dist/wizard.js.map +1 -1
- package/dist/wizards/gcp-firebase.d.ts +3 -0
- package/dist/wizards/gcp-firebase.d.ts.map +1 -1
- package/dist/wizards/gcp-firebase.js +1 -0
- package/dist/wizards/gcp-firebase.js.map +1 -1
- package/dist/wizards/gcp-install-job.d.ts +74 -0
- package/dist/wizards/gcp-install-job.d.ts.map +1 -0
- package/dist/wizards/gcp-install-job.js +398 -0
- package/dist/wizards/gcp-install-job.js.map +1 -0
- package/dist/wizards/gcp-prompts.d.ts +3 -0
- package/dist/wizards/gcp-prompts.d.ts.map +1 -0
- package/dist/wizards/gcp-prompts.js +25 -0
- package/dist/wizards/gcp-prompts.js.map +1 -0
- package/dist/wizards/gcp-pulumi.d.ts +4 -2
- package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
- package/dist/wizards/gcp-pulumi.js +1 -1
- package/dist/wizards/gcp-pulumi.js.map +1 -1
- package/dist/wizards/gcp.d.ts +40 -0
- package/dist/wizards/gcp.d.ts.map +1 -1
- package/dist/wizards/gcp.js +223 -59
- package/dist/wizards/gcp.js.map +1 -1
- package/package.json +6 -4
- package/src/adapter.ts +97 -0
- package/src/adc-impersonation.ts +176 -0
- package/src/admin-domain.test.ts +254 -0
- package/src/admin-domain.ts +245 -0
- package/src/admin-memory.test.ts +151 -0
- package/src/agent/apply.test.ts +185 -0
- package/src/agent/apply.ts +124 -0
- package/src/agent/cli-provenance.test.ts +159 -0
- package/src/agent/cli-provenance.ts +191 -0
- package/src/agent/command.test.ts +77 -0
- package/src/agent/command.ts +564 -0
- package/src/agent/config.ts +86 -0
- package/src/agent/lock-config.test.ts +97 -0
- package/src/agent/lock.ts +110 -0
- package/src/agent/queue.test.ts +10 -0
- package/src/agent/queue.ts +91 -0
- package/src/agent/run.test.ts +437 -0
- package/src/agent/run.ts +295 -0
- package/src/agent/systemd.ts +209 -0
- package/src/agent/units.test.ts +223 -0
- package/src/agent/units.ts +163 -0
- package/src/azure-front-door.test.ts +119 -0
- package/src/azure-front-door.ts +56 -0
- package/src/bootstrap-token.ts +20 -0
- package/src/caddy.ts +112 -0
- package/src/cdn-copy.ts +91 -0
- package/src/cert-wait.test.ts +194 -0
- package/src/cert-wait.ts +163 -0
- package/src/checkpoint-secrets.test.ts +380 -0
- package/src/checkpoint-secrets.ts +283 -0
- package/src/cli-routing.test.ts +45 -0
- package/src/cli-routing.ts +43 -0
- package/src/cli.ts +1491 -0
- package/src/cloudsql-iam-user.ts +147 -0
- package/src/compose.ts +334 -0
- package/src/default-firewall.test.ts +411 -0
- package/src/default-firewall.ts +342 -0
- package/src/deployed-release.test.ts +231 -0
- package/src/deployed-release.ts +220 -0
- package/src/dns/cloudflare.ts +182 -0
- package/src/dns/groups.test.ts +76 -0
- package/src/dns/groups.ts +86 -0
- package/src/dns/index.ts +56 -0
- package/src/dns/manual.ts +130 -0
- package/src/dns/types.ts +27 -0
- package/src/gateway-credentials.test.ts +180 -0
- package/src/gateway-credentials.ts +144 -0
- package/src/gcloud-components.test.ts +76 -0
- package/src/gcloud-components.ts +85 -0
- package/src/gcloud-retry.ts +41 -0
- package/src/gcloud-runner.test.ts +61 -0
- package/src/gcloud.test.ts +99 -0
- package/src/gcloud.ts +322 -0
- package/src/gcp-names.ts +135 -0
- package/src/index.test.ts +332 -0
- package/src/index.ts +114 -0
- package/src/infra-contract.ts +32 -0
- package/src/install-job-runner.ts +166 -0
- package/src/install-job.test.ts +566 -0
- package/src/install-job.ts +542 -0
- package/src/install-state.test.ts +239 -0
- package/src/install-state.ts +583 -0
- package/src/internal-token.ts +35 -0
- package/src/lifecycle.ts +900 -0
- package/src/mcp-iap-stacks.test.ts +58 -0
- package/src/mcp-iap.test.ts +149 -0
- package/src/mcp-iap.ts +131 -0
- package/src/media-volume.test.ts +296 -0
- package/src/migration-runner.ts +365 -0
- package/src/operator-access-stacks.test.ts +55 -0
- package/src/operator-access.test.ts +332 -0
- package/src/operator-access.ts +554 -0
- package/src/ops-backend.test.ts +320 -0
- package/src/ops-backend.ts +414 -0
- package/src/provisioner-identity.test.ts +482 -0
- package/src/provisioner-identity.ts +407 -0
- package/src/redirects-emit.ts +175 -0
- package/src/region-data.ts +428 -0
- package/src/regions.test.ts +277 -0
- package/src/regions.ts +370 -0
- package/src/release-contract.ts +57 -0
- package/src/release-manifest.test.ts +300 -0
- package/src/release-manifest.ts +310 -0
- package/src/run-mode.ts +40 -0
- package/src/runtime-secrets.test.ts +627 -0
- package/src/runtime-secrets.ts +754 -0
- package/src/self-command.test.ts +14 -0
- package/src/self-command.ts +21 -0
- package/src/self-hosted-compose.test.ts +177 -0
- package/src/self-hosted-release.ts +240 -0
- package/src/self-hosted-roles.integration.test.ts +161 -0
- package/src/self-hosted-roles.test.ts +124 -0
- package/src/self-hosted-roles.ts +203 -0
- package/src/self-hosted-upgrade.test.ts +394 -0
- package/src/self-hosted-upgrade.ts +548 -0
- package/src/stack-contract-parity.test.ts +199 -0
- package/src/stack-contract.test.ts +391 -0
- package/src/stack-contract.ts +888 -0
- package/src/stack-converge-secrets.test.ts +421 -0
- package/src/stack-converge.test.ts +833 -0
- package/src/stack-converge.ts +909 -0
- package/src/staging-admin.test.ts +246 -0
- package/src/staging-admin.ts +324 -0
- package/src/state-attach.test.ts +211 -0
- package/src/state-attach.ts +132 -0
- package/src/state-backend.test.ts +145 -0
- package/src/state-backend.ts +262 -0
- package/src/state-command.ts +293 -0
- package/src/state-migrate.test.ts +479 -0
- package/src/state-migrate.ts +434 -0
- package/src/state-pulumi.ts +289 -0
- package/src/state-store.test.ts +212 -0
- package/src/state-store.ts +243 -0
- package/src/upgrade/app-roll.ts +486 -0
- package/src/upgrade/backup.ts +109 -0
- package/src/upgrade/converge.ts +437 -0
- package/src/upgrade/gcp-lookup.ts +167 -0
- package/src/upgrade/health.test.ts +279 -0
- package/src/upgrade/health.ts +305 -0
- package/src/upgrade/release-images.test.ts +119 -0
- package/src/upgrade/release-images.ts +393 -0
- package/src/upgrade/reporter.ts +46 -0
- package/src/upgrade/upgrade.test.ts +626 -0
- package/src/upgrade/upgrade.ts +249 -0
- package/src/upgrader-assess.test.ts +306 -0
- package/src/upgrader-assess.ts +250 -0
- package/src/upgrader-gcp.ts +123 -0
- package/src/upgrader-job.test.ts +411 -0
- package/src/upgrader-job.ts +502 -0
- package/src/upgrader-reporter.test.ts +46 -0
- package/src/upgrader-reporter.ts +105 -0
- package/src/upgrader-setup.test.ts +169 -0
- package/src/upgrader-setup.ts +355 -0
- package/src/wizard.ts +425 -0
- package/src/wizards/gcp-cost.ts +188 -0
- package/src/wizards/gcp-firebase.ts +72 -0
- package/src/wizards/gcp-install-job.ts +506 -0
- package/src/wizards/gcp-prompts.ts +26 -0
- package/src/wizards/gcp-pulumi.test.ts +246 -0
- package/src/wizards/gcp-pulumi.ts +516 -0
- package/src/wizards/gcp.ts +1935 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Installs, re-pins and removes the agent's systemd --user units (#696
|
|
4
|
+
* Phase 4c). Every command goes through a {@link HostRunner} and the unit
|
|
5
|
+
* directory is a parameter, so tests run against a fake and a temp dir —
|
|
6
|
+
* nothing here touches the real systemd of the machine running the tests.
|
|
7
|
+
*
|
|
8
|
+
* User units stop with the user's last session unless the user "lingers";
|
|
9
|
+
* {@link installAgentUnits} turns lingering on (allowed for oneself on
|
|
10
|
+
* systemd-logind's default policy) and, where it may not, says which one
|
|
11
|
+
* command an administrator runs.
|
|
12
|
+
*/
|
|
13
|
+
import { closeSync, ftruncateSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync, writeSync, } from "node:fs";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { agentServiceUnit, agentTimerUnit, pinnedAgentVersion, } from "./units.js";
|
|
17
|
+
/** The real host: this user's systemd user-unit directory. */
|
|
18
|
+
export function currentAgentHost(runner) {
|
|
19
|
+
const config = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
20
|
+
return {
|
|
21
|
+
runner,
|
|
22
|
+
unitDir: join(config, "systemd", "user"),
|
|
23
|
+
user: process.env.USER || process.env.LOGNAME || "",
|
|
24
|
+
platform: process.platform,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Why the agent cannot be installed on this host, or null. Linux with a
|
|
29
|
+
* systemd user manager is the supported target; macOS (Docker Desktop) is
|
|
30
|
+
* a development setup, and there `upgrade` is run by hand.
|
|
31
|
+
*/
|
|
32
|
+
export async function agentHostProblem(host) {
|
|
33
|
+
if (host.platform !== "linux") {
|
|
34
|
+
return `the update agent runs on Linux hosts with systemd (this is ${host.platform}). Install updates with the upgrade command instead.`;
|
|
35
|
+
}
|
|
36
|
+
const r = await host.runner.run(["systemctl", "--user", "show-environment"]);
|
|
37
|
+
if (r.exitCode !== 0) {
|
|
38
|
+
return `systemd's user manager is not reachable (\`systemctl --user\`: ${r.stderr.trim().slice(0, 200) || `exit ${r.exitCode}`}). Log in to the host as this user directly (not via \`su\`), or install updates with the upgrade command.`;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/** `<unitDir>/<name>.service` + `.timer`. */
|
|
43
|
+
export function agentUnitPaths(host, name) {
|
|
44
|
+
return {
|
|
45
|
+
service: join(host.unitDir, `${name}.service`),
|
|
46
|
+
timer: join(host.unitDir, `${name}.timer`),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function systemctl(host, ...args) {
|
|
50
|
+
const r = await host.runner.run(["systemctl", "--user", ...args]);
|
|
51
|
+
return r.exitCode === 0
|
|
52
|
+
? null
|
|
53
|
+
: `systemctl --user ${args.join(" ")}: ${r.stderr.trim().slice(0, 300) || `exit ${r.exitCode}`}`;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Write both units, reload, enable + start the timer, and make sure the
|
|
57
|
+
* user lingers. Idempotent: re-running rewrites the same files.
|
|
58
|
+
*/
|
|
59
|
+
export async function installAgentUnits(spec, host) {
|
|
60
|
+
const paths = agentUnitPaths(host, spec.name);
|
|
61
|
+
try {
|
|
62
|
+
mkdirSync(host.unitDir, { recursive: true });
|
|
63
|
+
writeFileSync(paths.service, agentServiceUnit(spec), { mode: 0o644 });
|
|
64
|
+
writeFileSync(paths.timer, agentTimerUnit(spec), { mode: 0o644 });
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
return { ok: false, error: `could not write the units: ${e.message}` };
|
|
68
|
+
}
|
|
69
|
+
for (const args of [["daemon-reload"], ["enable", "--now", `${spec.name}.timer`]]) {
|
|
70
|
+
const error = await systemctl(host, ...args);
|
|
71
|
+
if (error)
|
|
72
|
+
return { ok: false, error };
|
|
73
|
+
}
|
|
74
|
+
const warnings = [];
|
|
75
|
+
const linger = await ensureLinger(host);
|
|
76
|
+
if (linger)
|
|
77
|
+
warnings.push(linger);
|
|
78
|
+
return { ok: true, warnings };
|
|
79
|
+
}
|
|
80
|
+
/** Lingering on for the user; returns a warning when it could not be. */
|
|
81
|
+
async function ensureLinger(host) {
|
|
82
|
+
if (!host.user)
|
|
83
|
+
return "could not tell the user name, so lingering was not checked";
|
|
84
|
+
const show = await host.runner.run(["loginctl", "show-user", host.user, "--property=Linger"]);
|
|
85
|
+
if (show.exitCode === 0 && show.stdout.trim() === "Linger=yes")
|
|
86
|
+
return null;
|
|
87
|
+
const enable = await host.runner.run(["loginctl", "enable-linger", host.user]);
|
|
88
|
+
if (enable.exitCode === 0)
|
|
89
|
+
return null;
|
|
90
|
+
return `the agent only runs while ${host.user} is logged in: lingering could not be switched on (${enable.stderr.trim().slice(0, 200) || `exit ${enable.exitCode}`}). Have an administrator run \`sudo loginctl enable-linger ${host.user}\` once.`;
|
|
91
|
+
}
|
|
92
|
+
/** The release the installed service runs, or null when it is not installed. */
|
|
93
|
+
export function installedAgentVersion(host, name) {
|
|
94
|
+
const { service } = agentUnitPaths(host, name);
|
|
95
|
+
try {
|
|
96
|
+
return pinnedAgentVersion(readFileSync(service, "utf8"));
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
if (e.code === "ENOENT")
|
|
100
|
+
return null;
|
|
101
|
+
throw e;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Re-pin an installed agent to `spec.version` (after an update). Does
|
|
106
|
+
* nothing when the agent is not installed.
|
|
107
|
+
*/
|
|
108
|
+
export async function repinAgentUnits(spec, host) {
|
|
109
|
+
const { service } = agentUnitPaths(host, spec.name);
|
|
110
|
+
try {
|
|
111
|
+
// "r+" opens only an existing file (ENOENT otherwise), so the check and
|
|
112
|
+
// the rewrite are one step — no window for the file to change between.
|
|
113
|
+
const fd = openSync(service, "r+");
|
|
114
|
+
try {
|
|
115
|
+
ftruncateSync(fd, 0);
|
|
116
|
+
writeSync(fd, agentServiceUnit(spec), 0, "utf8");
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
closeSync(fd);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
if (e.code === "ENOENT") {
|
|
124
|
+
return { ok: true, warnings: [], installed: false };
|
|
125
|
+
}
|
|
126
|
+
return { ok: false, error: `could not rewrite ${service}: ${e.message}` };
|
|
127
|
+
}
|
|
128
|
+
const error = await systemctl(host, "daemon-reload");
|
|
129
|
+
return error ? { ok: false, error } : { ok: true, warnings: [], installed: true };
|
|
130
|
+
}
|
|
131
|
+
/** Stop + disable the timer and delete both units. */
|
|
132
|
+
export async function uninstallAgentUnits(host, name) {
|
|
133
|
+
const warnings = [];
|
|
134
|
+
const disabled = await systemctl(host, "disable", "--now", `${name}.timer`);
|
|
135
|
+
if (disabled)
|
|
136
|
+
warnings.push(disabled);
|
|
137
|
+
const paths = agentUnitPaths(host, name);
|
|
138
|
+
rmSync(paths.service, { force: true });
|
|
139
|
+
rmSync(paths.timer, { force: true });
|
|
140
|
+
const reload = await systemctl(host, "daemon-reload");
|
|
141
|
+
if (reload)
|
|
142
|
+
warnings.push(reload);
|
|
143
|
+
return { ok: true, warnings };
|
|
144
|
+
}
|
|
145
|
+
/** `systemctl --user status` of both units (text for the terminal). */
|
|
146
|
+
export async function agentUnitStatus(host, name) {
|
|
147
|
+
const r = await host.runner.run([
|
|
148
|
+
"systemctl",
|
|
149
|
+
"--user",
|
|
150
|
+
"--no-pager",
|
|
151
|
+
"status",
|
|
152
|
+
`${name}.timer`,
|
|
153
|
+
`${name}.service`,
|
|
154
|
+
]);
|
|
155
|
+
// status exits 3 for an inactive oneshot service; the text is what matters.
|
|
156
|
+
return (r.stdout || r.stderr).trim();
|
|
157
|
+
}
|
|
158
|
+
/** The argv that shows the agent's log (journald). */
|
|
159
|
+
export function agentLogsArgv(name, opts) {
|
|
160
|
+
return [
|
|
161
|
+
"journalctl",
|
|
162
|
+
"--user",
|
|
163
|
+
"--no-pager",
|
|
164
|
+
"-u",
|
|
165
|
+
`${name}.service`,
|
|
166
|
+
"-n",
|
|
167
|
+
String(opts.lines),
|
|
168
|
+
...(opts.follow ? ["-f"] : []),
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=systemd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"systemd.js","sourceRoot":"","sources":["../../src/agent/systemd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Everything the units name; all paths absolute. */
|
|
2
|
+
export interface AgentUnitSpec {
|
|
3
|
+
/** Unit base name (`caelo-agent-<domain>-<hash>`), see {@link agentUnitName}. */
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly domain: string;
|
|
6
|
+
/** The provisioning release the service runs. */
|
|
7
|
+
readonly version: string;
|
|
8
|
+
/** The `bun` binary (the one that ran `init`). */
|
|
9
|
+
readonly bunPath: string;
|
|
10
|
+
/** The install's `.caelo/` directory. */
|
|
11
|
+
readonly caeloDir: string;
|
|
12
|
+
/** Where `.caelo/` lives (the service's working directory). */
|
|
13
|
+
readonly workDir: string;
|
|
14
|
+
/** PATH for the service: docker, cosign and bun must be on it. */
|
|
15
|
+
readonly path: string;
|
|
16
|
+
/** DOCKER_HOST of the installing shell (rootless Docker), when set. */
|
|
17
|
+
readonly dockerHost?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The provenance-verified CLI of {@link version} (agent/cli-provenance.ts)
|
|
20
|
+
* — what an update re-pins the agent to. Without it the service runs the
|
|
21
|
+
* release the operator installed with (`bun x`, as they did by hand).
|
|
22
|
+
*/
|
|
23
|
+
readonly cliPath?: string;
|
|
24
|
+
}
|
|
25
|
+
/** The longest a single run (one full upgrade) may take before systemd stops it. */
|
|
26
|
+
export declare const AGENT_RUN_TIMEOUT_MINUTES = 80;
|
|
27
|
+
/**
|
|
28
|
+
* `caelo-agent-<domain>-<hash>`: one agent per install, several installs
|
|
29
|
+
* per host possible. The slug is for people; the hash of the install's
|
|
30
|
+
* `.caelo/` path keeps two installs apart even when their slugs collide
|
|
31
|
+
* (`a.b.example.com` vs `a-b.example.com`).
|
|
32
|
+
*/
|
|
33
|
+
export declare function agentUnitName(domain: string, caeloDir: string): string;
|
|
34
|
+
/** The package the service runs, pinned. */
|
|
35
|
+
export declare function agentPackageRef(version: string): string;
|
|
36
|
+
/** The argv of one agent run (also what `agent status` shows). */
|
|
37
|
+
export declare function agentRunArgv(spec: Pick<AgentUnitSpec, "bunPath" | "version" | "cliPath">): string[];
|
|
38
|
+
/** `<name>.service`. */
|
|
39
|
+
export declare function agentServiceUnit(spec: AgentUnitSpec): string;
|
|
40
|
+
/** `<name>.timer`. */
|
|
41
|
+
export declare function agentTimerUnit(spec: Pick<AgentUnitSpec, "name" | "domain">): string;
|
|
42
|
+
/** The release an installed service file runs, or null when it names none. */
|
|
43
|
+
export declare function pinnedAgentVersion(serviceUnit: string): string | null;
|
|
44
|
+
//# sourceMappingURL=units.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../../src/agent/units.ts"],"names":[],"mappings":"AA4BA,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,oFAAoF;AACpF,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAStE;AAED,4CAA4C;AAC5C,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKvD;AAmBD,kEAAkE;AAClE,wBAAgB,YAAY,CAC1B,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC,GAC3D,MAAM,EAAE,CAGV;AAED,wBAAwB;AACxB,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAuB5D;AAED,sBAAsB;AACtB,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAC,GAAG,MAAM,CAoBnF;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMrE"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
/**
|
|
3
|
+
* The systemd units of `caelo-agent` (#696 Phase 4c), as text. Pure, so the
|
|
4
|
+
* exact files are unit-tested; agent/systemd.ts installs them.
|
|
5
|
+
*
|
|
6
|
+
* Why a systemd --user timer on the host, and not a container: the agent
|
|
7
|
+
* has to run `docker compose` for the install, and the only way a container
|
|
8
|
+
* could is the Docker socket — root on the host for anything that gets into
|
|
9
|
+
* that container (Watchtower, the usual answer, was archived in 2025 and
|
|
10
|
+
* verifies no signatures). The agent instead runs as the operator who ran
|
|
11
|
+
* `init`, a member of the `docker` group like that operator already is to
|
|
12
|
+
* run Compose at all; nothing new gets Docker access, and no unit runs as
|
|
13
|
+
* root.
|
|
14
|
+
*
|
|
15
|
+
* Shape: a oneshot service (one poll: heartbeat, then claim and run at most
|
|
16
|
+
* one request) started by a timer every {@link PLATFORM_UPDATE_AGENT_POLL_SECONDS}
|
|
17
|
+
* seconds after the previous run ended — systemd never starts the service
|
|
18
|
+
* while a run (an upgrade can take many minutes) is still active.
|
|
19
|
+
*
|
|
20
|
+
* The service runs the provisioning CLI pinned to an exact release
|
|
21
|
+
* (`bun x --bun @caelo-cms/provisioning@<version> agent run`); a successful
|
|
22
|
+
* update re-pins it to the release it installed.
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
import { PLATFORM_UPDATE_AGENT_POLL_SECONDS } from "@caelo-cms/shared";
|
|
26
|
+
/** The longest a single run (one full upgrade) may take before systemd stops it. */
|
|
27
|
+
export const AGENT_RUN_TIMEOUT_MINUTES = 80;
|
|
28
|
+
/**
|
|
29
|
+
* `caelo-agent-<domain>-<hash>`: one agent per install, several installs
|
|
30
|
+
* per host possible. The slug is for people; the hash of the install's
|
|
31
|
+
* `.caelo/` path keeps two installs apart even when their slugs collide
|
|
32
|
+
* (`a.b.example.com` vs `a-b.example.com`).
|
|
33
|
+
*/
|
|
34
|
+
export function agentUnitName(domain, caeloDir) {
|
|
35
|
+
const slug = domain
|
|
36
|
+
.toLowerCase()
|
|
37
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
38
|
+
.replace(/^-+|-+$/g, "")
|
|
39
|
+
.slice(0, 60);
|
|
40
|
+
if (!slug)
|
|
41
|
+
throw new Error(`cannot name the agent unit after domain ${JSON.stringify(domain)}`);
|
|
42
|
+
const hash = createHash("sha256").update(caeloDir).digest("hex").slice(0, 8);
|
|
43
|
+
return `caelo-agent-${slug}-${hash}`;
|
|
44
|
+
}
|
|
45
|
+
/** The package the service runs, pinned. */
|
|
46
|
+
export function agentPackageRef(version) {
|
|
47
|
+
if (!/^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$/.test(version)) {
|
|
48
|
+
throw new Error(`not a release version: ${JSON.stringify(version)}`);
|
|
49
|
+
}
|
|
50
|
+
return `@caelo-cms/provisioning@${version}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* One systemd word: double-quoted, `%` escaped (systemd specifiers). Values
|
|
54
|
+
* with a quote, backslash or control character are refused rather than
|
|
55
|
+
* escaped — they are paths and versions, and such a path is a mistake.
|
|
56
|
+
*/
|
|
57
|
+
function word(value) {
|
|
58
|
+
if (!value || /["\\\p{Cc}]/u.test(value)) {
|
|
59
|
+
throw new Error(`cannot put ${JSON.stringify(value)} into a systemd unit`);
|
|
60
|
+
}
|
|
61
|
+
return `"${value.replace(/%/g, "%%")}"`;
|
|
62
|
+
}
|
|
63
|
+
/** `Environment="KEY=value"`. */
|
|
64
|
+
function environment(key, value) {
|
|
65
|
+
return `Environment=${word(`${key}=${value}`)}`;
|
|
66
|
+
}
|
|
67
|
+
/** The argv of one agent run (also what `agent status` shows). */
|
|
68
|
+
export function agentRunArgv(spec) {
|
|
69
|
+
if (spec.cliPath)
|
|
70
|
+
return [spec.bunPath, spec.cliPath, "agent", "run"];
|
|
71
|
+
return [spec.bunPath, "x", "--bun", agentPackageRef(spec.version), "agent", "run"];
|
|
72
|
+
}
|
|
73
|
+
/** `<name>.service`. */
|
|
74
|
+
export function agentServiceUnit(spec) {
|
|
75
|
+
return [
|
|
76
|
+
"# SPDX-License-Identifier: MPL-2.0",
|
|
77
|
+
"# Generated by cms-provision (agent install) — do not edit; re-run it instead.",
|
|
78
|
+
"[Unit]",
|
|
79
|
+
`Description=Caelo update agent for ${spec.domain}`,
|
|
80
|
+
"Documentation=https://caelo-cms.com/install-self-hosted",
|
|
81
|
+
"After=network-online.target",
|
|
82
|
+
"",
|
|
83
|
+
"[Service]",
|
|
84
|
+
"Type=oneshot",
|
|
85
|
+
`WorkingDirectory=${word(spec.workDir)}`,
|
|
86
|
+
environment("CAELO_SELF_HOSTED_DIR", spec.caeloDir),
|
|
87
|
+
environment("PATH", spec.path),
|
|
88
|
+
...(spec.dockerHost ? [environment("DOCKER_HOST", spec.dockerHost)] : []),
|
|
89
|
+
`ExecStart=${agentRunArgv(spec).map(word).join(" ")}`,
|
|
90
|
+
`TimeoutStartSec=${AGENT_RUN_TIMEOUT_MINUTES}min`,
|
|
91
|
+
// Backups, config.json and the lock file: the operator's only.
|
|
92
|
+
"UMask=0077",
|
|
93
|
+
"NoNewPrivileges=yes",
|
|
94
|
+
"Nice=5",
|
|
95
|
+
"",
|
|
96
|
+
].join("\n");
|
|
97
|
+
}
|
|
98
|
+
/** `<name>.timer`. */
|
|
99
|
+
export function agentTimerUnit(spec) {
|
|
100
|
+
return [
|
|
101
|
+
"# SPDX-License-Identifier: MPL-2.0",
|
|
102
|
+
"# Generated by cms-provision (agent install) — do not edit; re-run it instead.",
|
|
103
|
+
"[Unit]",
|
|
104
|
+
`Description=Poll for Caelo updates for ${spec.domain}`,
|
|
105
|
+
"",
|
|
106
|
+
"[Timer]",
|
|
107
|
+
// The first poll a minute after the timer starts (install, boot);
|
|
108
|
+
// then a minute after each run ends.
|
|
109
|
+
"OnActiveSec=1min",
|
|
110
|
+
"OnBootSec=2min",
|
|
111
|
+
`OnUnitInactiveSec=${PLATFORM_UPDATE_AGENT_POLL_SECONDS}s`,
|
|
112
|
+
"AccuracySec=5s",
|
|
113
|
+
`Unit=${spec.name}.service`,
|
|
114
|
+
"",
|
|
115
|
+
"[Install]",
|
|
116
|
+
"WantedBy=timers.target",
|
|
117
|
+
"",
|
|
118
|
+
].join("\n");
|
|
119
|
+
}
|
|
120
|
+
/** The release an installed service file runs, or null when it names none. */
|
|
121
|
+
export function pinnedAgentVersion(serviceUnit) {
|
|
122
|
+
const exec = /^ExecStart=.*$/m.exec(serviceUnit)?.[0] ?? "";
|
|
123
|
+
const m = /@caelo-cms\/provisioning@([0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?)"/.exec(exec) ??
|
|
124
|
+
/\/caelo-([0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?)\/node_modules\//.exec(exec);
|
|
125
|
+
return m?.[1] ?? null;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=units.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"units.js","sourceRoot":"","sources":["../../src/agent/units.ts"],"names":[],"mappings":"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"}
|