This is an automated email from the ASF dual-hosted git repository. wu-sheng pushed a commit to branch feat/template-modes-env-config in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git
commit 05da4a0a636c3fd3e7bc7c3f00869dedaa7bdb53 Author: Wu Sheng <[email protected]> AuthorDate: Fri Jun 26 10:14:47 2026 +0800 fix(infra-3d): remove the silent disk-bundle fallback from the live render path The 3D map was the lone surface that silently served the disk bundle when its remote couldn't be resolved — in live mode an unreachable ui_template store (or a missing / invalid remote row) returned the bundled config instead of blocking. Every other surface (bundle.ts) renders ONLY from remote rows and treats an unsynced / disabled / unreachable template as simply absent. resolveEffectiveConfig now does the same: render only from the remote row — a real OAP row in live mode, the synthetic bundled row in readonly — and return null otherwise. The route 503s on null, so the map blocks with its connectivity state (like layer/overview/alert) instead of masking an outage with a stale bundle. The bundle still reaches OAP via boot-seed and is served in readonly through the fake-remote row; it is never a live runtime fallback. Validated live: readonly 200 (bundle via fake-remote), live+reachable 200 (remote), live+ui_template-unreachable 503 (blocks). 162 BFF + 116 UI tests green. --- apps/bff/src/http/config/infra-3d.ts | 51 +++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/apps/bff/src/http/config/infra-3d.ts b/apps/bff/src/http/config/infra-3d.ts index 84ac1f4..f6c87e5 100644 --- a/apps/bff/src/http/config/infra-3d.ts +++ b/apps/bff/src/http/config/infra-3d.ts @@ -18,12 +18,13 @@ /** * `GET /api/infra-3d/config` — the EFFECTIVE 3D Infrastructure Map config * the `/3d/map` view consumes. The config is a first-class template kind - * (`horizon.infra-3d.config`), so it follows the same bundled → remote - * policy as every other template: when OAP holds a non-disabled remote - * row, that wins; otherwise the bundled default is served. Writes do NOT - * happen here — the admin editor publishes through the generic template - * sync surface (`POST /api/admin/templates/save`), which validates the - * 3D-map content before it reaches OAP. + * (`horizon.infra-3d.config`) and is read ONLY from the remote row, exactly + * like the layer/overview surfaces — never the disk bundle as a runtime + * fallback. The bundle reaches OAP via boot-seed; readonly mode renders it + * through the synthetic bundled row. When the ui_template store is unreachable + * (or the row is absent / invalid) this 503s so the map blocks, rather than + * silently serving a stale bundle. Writes do NOT happen here — the admin editor + * publishes through the generic template sync surface (`POST /api/admin/templates/save`). */ import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'; @@ -38,7 +39,6 @@ import { parseEnvelope, INFRA3D_CONFIG_KEY, } from '../../logic/templates/names.js'; -import { loadBundledInfra3dConfig } from '../../logic/infra-3d/bundled.js'; import { validateInfra3dConfig } from '../../logic/infra-3d/validate.js'; import type { Infra3dConfig } from '../../logic/infra-3d/types.js'; import { logger } from '../../logger.js'; @@ -60,6 +60,16 @@ export function registerInfra3dConfigRoutes( { preHandler: auth }, async (_req: FastifyRequest, reply: FastifyReply) => { const cfg = await resolveEffectiveConfig(deps); + // Live + ui_template store unreachable → block, exactly like every other + // surface (the UI renders its connectivity-empty state). The 3D map used + // to be the lone exception that silently served the bundle here. + if (!cfg) { + return reply.code(503).send({ + error: 'template_store_unreachable', + reason: + "OAP's ui_template store is unreachable; in live mode the 3D map renders only from OAP (no bundled fallback). It recovers when the store is reachable, or run templates.mode=readonly to serve the bundle.", + }); + } // The metric fan-out budget is OPERATIONAL (per-deployment, hot- // reloaded), so it lives in horizon.yaml — NOT the published template. // Inject it server-side so the UI keeps reading `cfg.pipeline.*`; this @@ -79,18 +89,26 @@ export function registerInfra3dConfigRoutes( ); } -/** Remote-wins resolution, mirroring `pickLayerContent` in the config - * bundle. The remote envelope is re-validated defensively — a row - * hand-edited on OAP into an invalid shape falls back to bundled rather - * than breaking the map. */ -async function resolveEffectiveConfig(deps: Infra3dConfigRouteDeps): Promise<Infra3dConfig> { - const bundled = loadBundledInfra3dConfig(); +/** + * Render ONLY from the remote row — never the disk bundle as a runtime + * fallback. This matches the layer/overview surfaces (bundle.ts): the bundle + * reaches OAP via boot-seed, then is read back as a remote row. `getSyncStatus` + * supplies that row — a real OAP row in live mode, the synthetic bundled row in + * readonly — so readonly renders the bundle through the SAME remote path. + * + * Returns null (→ the route blocks with 503) when there is no usable remote + * row: the store is unreachable, the row is absent / not-yet-seeded / disabled, + * or the remote envelope is invalid. A live ui_template outage must surface as + * a block like every other surface, not a silent stale bundle. + */ +async function resolveEffectiveConfig(deps: Infra3dConfigRouteDeps): Promise<Infra3dConfig | null> { try { const sync = await getSyncStatus({ client: deps.uiTemplateClient(), bundled: () => iterateBundledTemplates(), logger, }); + if (sync.unreachable) return null; const name = formatName('infra-3d', INFRA3D_CONFIG_KEY); const row = sync.rows.find((r) => r.name === name); if (row && row.status !== 'disabled' && row.effective === 'remote' && row.remote) { @@ -98,11 +116,12 @@ async function resolveEffectiveConfig(deps: Infra3dConfigRouteDeps): Promise<Inf if (env) { const v = validateInfra3dConfig(env.content); if (v.ok) return v.value; - logger.warn({ issues: v.issues }, 'remote infra-3d config invalid; using bundled'); + logger.warn({ issues: v.issues }, 'remote infra-3d config invalid — blocking, not falling back'); } } + return null; } catch (err) { - logger.warn({ err }, 'infra-3d effective resolve failed; using bundled'); + logger.warn({ err }, 'infra-3d effective resolve failed — blocking, not falling back'); + return null; } - return bundled; }
