@codeyam-editor/codeyam-editor 0.1.4 → 0.1.6
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/npm/cli.js +5 -1
- package/npm/cloud-branch-switch.js +1 -1
- package/npm/cloud.js +3166 -150
- package/npm/codesign-plan.js +68 -0
- package/npm/container.js +3 -0
- package/npm/disk-pressure.js +78 -0
- package/npm/editor-dev.js +96 -15
- package/npm/editor.js +85 -37
- package/npm/empty-project-dir.js +92 -7
- package/npm/fleet-add-failure.js +252 -0
- package/npm/fleet-asset-version.js +37 -0
- package/npm/fleet-auth-health.js +213 -0
- package/npm/fleet-auth.js +6 -1
- package/npm/fleet-build-data.js +109 -5
- package/npm/fleet-cleanup.js +232 -16
- package/npm/fleet-destroy-safety.js +272 -0
- package/npm/fleet-early-roster.js +87 -0
- package/npm/fleet-effect-poll.js +110 -0
- package/npm/fleet-effect-status.js +33 -32
- package/npm/fleet-env.js +86 -0
- package/npm/fleet-ghcr-secret.js +129 -0
- package/npm/fleet-git-maintenance.js +142 -0
- package/npm/fleet-host-op-limiter.js +68 -0
- package/npm/fleet-host.js +74 -0
- package/npm/fleet-iap-token.js +165 -0
- package/npm/fleet-job-badge.js +1 -1
- package/npm/fleet-launchd.js +171 -0
- package/npm/fleet-live-status.js +155 -0
- package/npm/fleet-memory.js +245 -9
- package/npm/fleet-plans.js +233 -4
- package/npm/fleet-pool.js +75 -16
- package/npm/fleet-prewarm.js +241 -0
- package/npm/fleet-project-drift.js +58 -0
- package/npm/fleet-public-url.js +394 -0
- package/npm/fleet-queue.js +95 -1
- package/npm/fleet-reachability-debounce.js +69 -0
- package/npm/fleet-reachability.js +80 -0
- package/npm/fleet-reconcile.js +43 -1
- package/npm/fleet-reset.js +330 -13
- package/npm/fleet-roster.js +6 -8
- package/npm/fleet-running-plan.js +85 -0
- package/npm/fleet-self-update.js +142 -0
- package/npm/fleet-session-token-cache.js +65 -0
- package/npm/fleet-setup-steps.js +112 -0
- package/npm/fleet-sleep-status.js +33 -0
- package/npm/fleet-status-refs.js +96 -0
- package/npm/fleet-strand.js +181 -0
- package/npm/fleet-stuck-status.js +117 -0
- package/npm/fleet-vm-access.js +404 -0
- package/npm/fleet-vm-probe-target.js +282 -0
- package/npm/fleet-vm-readiness.js +237 -0
- package/npm/fleet-vm-slugs.js +116 -0
- package/npm/google-ingress.js +226 -0
- package/npm/ingress-invoke.js +770 -0
- package/npm/launch-config.js +386 -9
- package/npm/scenario-check.js +880 -37
- package/npm/scenario-handlers.js +154 -30
- package/npm/scenario-interactivity.js +145 -6
- package/npm/scenario-issues.js +13 -1
- package/npm/scenario-metrics.js +116 -1
- package/npm/scenario-mocks.js +144 -21
- package/npm/scenario-playwright.js +649 -38
- package/npm/script-drift.js +152 -0
- package/npm/utils.js +365 -52
- package/npm/vm-urls.js +69 -8
- package/package.json +5 -5
- package/ui/dist/assets/ContainerInspector-DgM0pTAx.js +2 -0
- package/ui/dist/assets/EditorShell-B7Q06uJ_.js +252 -0
- package/ui/dist/assets/EditorShell-DP5UY20G.css +1 -0
- package/ui/dist/assets/{Markdown-BHBqz-Mj.js → Markdown-Chgdf3PW.js} +1 -1
- package/ui/dist/assets/ProjectLauncherScreen-BRrEr8Ng.js +1 -0
- package/ui/dist/assets/ScenarioDataPanel-BeQfa28g.js +9 -0
- package/ui/dist/assets/{ScenarioDataPanel-DYP7pi_n.css → ScenarioDataPanel-C1LukYI6.css} +1 -1
- package/ui/dist/assets/ScenarioDataPanelFullPage-BLqRCxQX.js +1 -0
- package/ui/dist/assets/ScenarioRenderer-CIAxfHPh.js +21 -0
- package/ui/dist/assets/StepsSimulator-CymGgfbu.js +1 -0
- package/ui/dist/assets/index-BygCiJSb.css +1 -0
- package/ui/dist/assets/{index-BuK0lizt.js → index-DCHqRrwO.js} +9 -9
- package/ui/dist/assets/{markdown-v0F0UEt3.js → markdown-C9tbsVHX.js} +1 -1
- package/ui/dist/assets/react-nrLBr15I.js +28 -0
- package/ui/dist/assets/useEvents-S3Qk1Y_d.js +1 -0
- package/ui/dist/assets/useServerIdentity-B7rI0u3O.js +1 -0
- package/ui/dist/codeyam-mark.svg +4 -0
- package/ui/dist/codeyam-wordmark.svg +5 -0
- package/ui/dist/index.html +4 -4
- package/ui/dist/open-source/codeyam-counter.png +0 -0
- package/ui/dist/open-source/el-carot.png +0 -0
- package/ui/dist/open-source/tabcommand.png +0 -0
- package/ui/dist/assets/ContainerInspector-C8EkH4PP.js +0 -2
- package/ui/dist/assets/EditorShell-tMMdarZD.js +0 -187
- package/ui/dist/assets/ScenarioDataPanel-xWYGZOpo.js +0 -5
- package/ui/dist/assets/ScenarioDataPanelFullPage-5G6KuZgw.js +0 -1
- package/ui/dist/assets/ScenarioRenderer-CSdhX1Ak.js +0 -1
- package/ui/dist/assets/StepsSimulator-CRtmlyNZ.js +0 -1
- package/ui/dist/assets/index-BGqairFV.css +0 -1
- package/ui/dist/assets/react-CSS0HapR.js +0 -28
- package/ui/dist/assets/useEvents-DggWrtHe.js +0 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Deterministic Google Cloud HTTPS-LB resource-name derivation for the fleet.
|
|
3
|
+
//
|
|
4
|
+
// The fleet's public browser ingress moves from cloudflared named tunnels to a
|
|
5
|
+
// single Google external HTTPS Load Balancer (one URL map, host-based routing).
|
|
6
|
+
// Reaching a specific port on a specific VM needs a zonal NEG + backend service
|
|
7
|
+
// per (VM, surface); the URL map then routes each `vm-<N>.<surface>.<domain>`
|
|
8
|
+
// host to that backend. This module is the single source of truth for those
|
|
9
|
+
// Google resource NAMES so the bash provisioners (scripts/gcp/google-ingress.sh,
|
|
10
|
+
// scripts/gcp/vm-ingress.sh) and any node caller derive identical strings and a
|
|
11
|
+
// unit test guards them against drift — exactly the contract npm/vm-urls.js and
|
|
12
|
+
// npm/launch-config.js follow.
|
|
13
|
+
//
|
|
14
|
+
// Pure logic only — no I/O, no env reads beyond the explicit `env` argument a
|
|
15
|
+
// caller passes in, so it stays unit-testable in isolation
|
|
16
|
+
// (npm/google-ingress.test.js). The HOSTNAMES it routes to are reused verbatim
|
|
17
|
+
// from npm/vm-urls.js (vmHostnames / dashboardHostname): the Google migration is
|
|
18
|
+
// a serving-layer swap, not a URL-scheme change, so hostnames must never drift
|
|
19
|
+
// from the cert/DNS that vm-urls.js already defines.
|
|
20
|
+
//
|
|
21
|
+
// Stack note: this is fleet-operator infrastructure (the cloud GCE fleet that
|
|
22
|
+
// runs codeyam-editor itself), not a per-app-stack assumption. A project that
|
|
23
|
+
// never joins a Google fleet never sets FLEET_DOMAIN / FLEET_INGRESS=google and
|
|
24
|
+
// never derives a name here; the legacy cloudflared path stays the fallback.
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
normalizeVmNumber,
|
|
28
|
+
vmHostnames,
|
|
29
|
+
dashboardHostname,
|
|
30
|
+
} = require('./vm-urls');
|
|
31
|
+
|
|
32
|
+
// The two per-VM ingress surfaces. `editor` fronts the in-VM editor control API
|
|
33
|
+
// (a fixed port); `preview` fronts the VM's dev server (a per-project port).
|
|
34
|
+
// Keep this list as the single definition both name derivation and the host-rule
|
|
35
|
+
// builder iterate, so a third surface is one edit, not three.
|
|
36
|
+
const SURFACES = Object.freeze(['editor', 'preview']);
|
|
37
|
+
|
|
38
|
+
// The in-VM editor control API port. Fixed across the fleet (matches
|
|
39
|
+
// named-tunnel-config.sh's EDITOR_PORT default and cloud.js's `-L …:14199`), so
|
|
40
|
+
// the editor backend NEG always targets this port regardless of project.
|
|
41
|
+
const EDITOR_PORT = 14199;
|
|
42
|
+
|
|
43
|
+
/** The fixed in-VM editor control-API port the editor backend NEG targets. A
|
|
44
|
+
* function (not a bare constant export) so callers read it the same way they
|
|
45
|
+
* read the per-VM appPort — a uniform "resolve the port for this surface" shape. */
|
|
46
|
+
function editorPort() {
|
|
47
|
+
return EDITOR_PORT;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Validate an ingress surface against SURFACES. The NEG / backend-service /
|
|
51
|
+
* host-rule derivations all key off it, so a typo'd surface would mint a
|
|
52
|
+
* resource that nothing routes to — reject it loudly at the boundary, mirroring
|
|
53
|
+
* how vm-urls.js rejects a bad VM number or domain. Returns the surface. */
|
|
54
|
+
function normalizeSurface(surface) {
|
|
55
|
+
if (!SURFACES.includes(surface)) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`surface must be one of ${SURFACES.join('|')}, got: ${surface}`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return surface;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The zonal NEG name for a (VM, surface): `vm-<N>-<surface>-neg`. One network
|
|
64
|
+
* endpoint group per surface points at the VM's internal IP + that surface's
|
|
65
|
+
* port — the Google-native analog of a cloudflared ingress rule's local origin. */
|
|
66
|
+
function negName(n, surface) {
|
|
67
|
+
const num = normalizeVmNumber(n);
|
|
68
|
+
return `vm-${num}-${normalizeSurface(surface)}-neg`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The backend-service name for a (VM, surface): `vm-<N>-<surface>-bes`. One
|
|
72
|
+
* backend service per host rule wraps the matching NEG (with IAP + a health
|
|
73
|
+
* check); the URL map's host rule points at this name. */
|
|
74
|
+
function backendServiceName(n, surface) {
|
|
75
|
+
const num = normalizeVmNumber(n);
|
|
76
|
+
return `vm-${num}-${normalizeSurface(surface)}-bes`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The URL-map host rules for a VM: one `{ host, surface, backendService }` per
|
|
80
|
+
* surface, where `host` is the EXACT hostname vm-urls.js derives (so the LB
|
|
81
|
+
* routes the same hostnames the cert covers and DNS resolves) and
|
|
82
|
+
* `backendService` is this module's name for that surface. Reusing
|
|
83
|
+
* `vmHostnames` rather than re-deriving keeps the hostname scheme single-sourced.
|
|
84
|
+
* Returns the surfaces in SURFACES order so the provisioner's output is stable.
|
|
85
|
+
*
|
|
86
|
+
* An optional `slug` overrides the routed `host` with the VM's custom hostname
|
|
87
|
+
* (`<slug>.<surface>.<domain>`) while the NEG / `backendService` names stay
|
|
88
|
+
* keyed off the VM NUMBER — the slug changes only what the URL map matches, not
|
|
89
|
+
* the internal GCP resource names. */
|
|
90
|
+
function urlMapHostRules(n, domain, slug) {
|
|
91
|
+
const hosts = vmHostnames(n, domain, slug); // also validates n, domain, slug
|
|
92
|
+
return SURFACES.map((surface) => ({
|
|
93
|
+
host: hosts[surface],
|
|
94
|
+
surface,
|
|
95
|
+
backendService: backendServiceName(n, surface),
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Decide which gcloud URL-map operation reconciles a single `(host,
|
|
100
|
+
* pathMatcher)` against the map's CURRENT state, given `hostRules` — the
|
|
101
|
+
* flattened `[{ host, pathMatcher }]` projection of the URL map's host rules
|
|
102
|
+
* (one entry per host, since one host rule can list several hosts). Returns
|
|
103
|
+
* `{ op }`, one of:
|
|
104
|
+
* - `skip` — `host` already routes to `pathMatcher`; nothing to do.
|
|
105
|
+
* - `add-host-rule` — `pathMatcher` already exists (some other host points
|
|
106
|
+
* at it, e.g. the numeric `vmN.<surface>` host) but
|
|
107
|
+
* `host` is not attached: attach it. Fixes the
|
|
108
|
+
* `Duplicate path matcher` failure on a reused slug.
|
|
109
|
+
* - `repoint` — `host` exists but aims at a DIFFERENT matcher (a stale
|
|
110
|
+
* rule left by a destroyed VM): remove it, then attach
|
|
111
|
+
* `host` to the correct matcher. Fixes a reused slug
|
|
112
|
+
* silently skipped as "already configured" while it
|
|
113
|
+
* still routed to a dead backend.
|
|
114
|
+
* - `add-path-matcher`— neither `host` nor `pathMatcher` is present: create
|
|
115
|
+
* the matcher and attach the host (the original path).
|
|
116
|
+
*
|
|
117
|
+
* Pure and side-effect-free — no gcloud, no I/O — so vm-ingress.sh can read the
|
|
118
|
+
* URL-map state in bash, ask this helper for the op, and run the matching
|
|
119
|
+
* gcloud command, keeping the four-way decision unit-testable (the same
|
|
120
|
+
* bash↔node contract the name derivations above follow). Tolerates a
|
|
121
|
+
* missing/empty `hostRules`. */
|
|
122
|
+
function hostRuleOperation({ hostRules, host, pathMatcher } = {}) {
|
|
123
|
+
if (!host) throw new Error('hostRuleOperation requires a host');
|
|
124
|
+
if (!pathMatcher) throw new Error('hostRuleOperation requires a pathMatcher');
|
|
125
|
+
const rules = Array.isArray(hostRules) ? hostRules : [];
|
|
126
|
+
const existing = rules.find((r) => r && r.host === host);
|
|
127
|
+
if (existing) {
|
|
128
|
+
return { op: existing.pathMatcher === pathMatcher ? 'skip' : 'repoint' };
|
|
129
|
+
}
|
|
130
|
+
const matcherExists = rules.some((r) => r && r.pathMatcher === pathMatcher);
|
|
131
|
+
return { op: matcherExists ? 'add-host-rule' : 'add-path-matcher' };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Decide whether a NEG must be attached as a backend to its backend service,
|
|
135
|
+
* given `backends` — the BES's CURRENT `backends[].group` self-links (what
|
|
136
|
+
* `gcloud compute backend-services describe --format='value(backends[].group)'`
|
|
137
|
+
* emits) — and `neg`, the bare NEG name this surface expects. Returns `{ op }`:
|
|
138
|
+
* - `skip` — some current backend already resolves to `neg`; nothing to do.
|
|
139
|
+
* - `attach` — `neg` is not yet a backend (an empty BES, or one wired to a
|
|
140
|
+
* different group): `add-backend` it.
|
|
141
|
+
*
|
|
142
|
+
* `backends[].group` is a full self-link
|
|
143
|
+
* (`.../networkEndpointGroups/vm-3-editor-neg`), so the match compares by
|
|
144
|
+
* basename — the bare NEG name — not the whole URL. Pure and side-effect-free
|
|
145
|
+
* (no gcloud, no I/O), so vm-ingress.sh reads the BES's backends in bash, asks
|
|
146
|
+
* this helper for the op, and `add-backend`s only when it returns `attach`: the
|
|
147
|
+
* same bash↔node contract `hostRuleOperation` follows. This is what makes `up`
|
|
148
|
+
* *converge* a BES that pre-exists with zero backends (the permanent-503 empty-
|
|
149
|
+
* backend bug) instead of skipping the attach as "already configured". Throws on
|
|
150
|
+
* a missing `neg` (mirrors `hostRuleOperation`'s guard); tolerates a
|
|
151
|
+
* missing/empty `backends`. */
|
|
152
|
+
function backendNegAttachOperation({ backends, neg } = {}) {
|
|
153
|
+
if (!neg) throw new Error('backendNegAttachOperation requires a neg');
|
|
154
|
+
const groups = Array.isArray(backends) ? backends : [];
|
|
155
|
+
const basename = (g) => String(g).split('/').pop();
|
|
156
|
+
const attached = groups.some((g) => g && basename(g) === neg);
|
|
157
|
+
return { op: attached ? 'skip' : 'attach' };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** The dashboard's backend-service name: `dashboard-bes`. The dashboard is a
|
|
161
|
+
* single fleet-wide surface (not per-VM), so its name carries no VM number. The
|
|
162
|
+
* backend itself is only fully wired once the dashboard moves onto GCE
|
|
163
|
+
* (vm-dashboard-on-gce), but the name is defined here so the shared provisioner
|
|
164
|
+
* and that dependent plan agree. */
|
|
165
|
+
function dashboardBackendServiceName() {
|
|
166
|
+
return 'dashboard-bes';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** The dashboard's zonal NEG name: `dashboard-neg`. One network endpoint group
|
|
170
|
+
* points at the operator host's internal IP + the dashboard port (8787) — the
|
|
171
|
+
* Google-native analog of the dashboard's old ngrok upstream. Single fleet-wide
|
|
172
|
+
* surface, so (like the backend-service name) it carries no VM number. Wired by
|
|
173
|
+
* scripts/gcp/dashboard-ingress.sh once the dashboard moves onto GCE. */
|
|
174
|
+
function dashboardNegName() {
|
|
175
|
+
return 'dashboard-neg';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** The dashboard's dedicated health-check name: `dashboard-hc`. The per-VM
|
|
179
|
+
* surfaces share `fleet-ingress-hc` (port 14199), but the dashboard backend
|
|
180
|
+
* health-checks its own port (8787, path /healthz), so it needs its own check
|
|
181
|
+
* rather than reusing the editor-port one. */
|
|
182
|
+
function dashboardHealthCheckName() {
|
|
183
|
+
return 'dashboard-hc';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** The dashboard's single URL-map host rule: `{ host, backendService }`, where
|
|
187
|
+
* `host` is `dashboard.<domain>` from vm-urls.js. Separate from
|
|
188
|
+
* `urlMapHostRules` because the dashboard is fleet-wide, not keyed off a VM
|
|
189
|
+
* number. */
|
|
190
|
+
function dashboardHostRule(domain) {
|
|
191
|
+
return {
|
|
192
|
+
host: dashboardHostname(domain), // validates domain
|
|
193
|
+
backendService: dashboardBackendServiceName(),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Extract the VM number from an LB-ingress resource name this module mints
|
|
198
|
+
* (`vm-<N>-<surface>-neg` or `vm-<N>-<surface>-bes`, surface ∈ SURFACES).
|
|
199
|
+
* Returns the number, or null for any name that isn't a per-VM ingress resource
|
|
200
|
+
* (the shared `codeyam-dashboard-*` backend, a foreign resource, junk). The
|
|
201
|
+
* inverse of negName / backendServiceName — the orphan sweep uses it to map the
|
|
202
|
+
* live LB backends back to the VM numbers that own them. The surface alternation
|
|
203
|
+
* is built from SURFACES so it stays in lockstep with the name derivations.
|
|
204
|
+
* Pure. */
|
|
205
|
+
function vmNumberFromResourceName(name) {
|
|
206
|
+
const re = new RegExp(`^vm-(\\d+)-(?:${SURFACES.join('|')})-(?:neg|bes)$`);
|
|
207
|
+
const m = re.exec(String(name == null ? '' : name).trim());
|
|
208
|
+
return m ? Number(m[1]) : null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
module.exports = {
|
|
212
|
+
SURFACES,
|
|
213
|
+
EDITOR_PORT,
|
|
214
|
+
editorPort,
|
|
215
|
+
normalizeSurface,
|
|
216
|
+
negName,
|
|
217
|
+
backendServiceName,
|
|
218
|
+
vmNumberFromResourceName,
|
|
219
|
+
urlMapHostRules,
|
|
220
|
+
hostRuleOperation,
|
|
221
|
+
backendNegAttachOperation,
|
|
222
|
+
dashboardBackendServiceName,
|
|
223
|
+
dashboardNegName,
|
|
224
|
+
dashboardHealthCheckName,
|
|
225
|
+
dashboardHostRule,
|
|
226
|
+
};
|