fix(mesh): apply D-1 pushed override on pilot deploys via file-presence fallback (#1019)

When isMeshStackEnabled returns false, ensureStackOverride now checks for
an override file already on disk before returning null. On pilot nodes the
mesh_stacks table is intentionally empty (opt-in state lives on central per
the C-3 design), so the DB gate blocked the override that central had already
pushed via applyLocalOverride. File-presence is safe as the fallback because
removeOverrideFromNode sends a DELETE to the pilot when a stack is opted out,
so a stale file cannot survive past opt-out.

Also aligns removeStackOverride to use path.basename consistently with all
other override-path construction sites in the same file.

Adds two tests: pilot node with a pushed file returns the path; pilot node
with no file returns null.
This commit is contained in:
Anso
2026-05-10 01:29:21 -04:00
committed by GitHub
parent 4c83d4e800
commit c12b3ba995
2 changed files with 57 additions and 3 deletions
+16 -3
View File
@@ -592,13 +592,26 @@ export class MeshService extends EventEmitter implements MeshForwarderHost {
public async ensureStackOverride(nodeId: number, stackName: string): Promise<string | null> {
if (!isValidStackName(stackName)) return null;
const db = DatabaseService.getInstance();
if (!db.isMeshStackEnabled(nodeId, stackName)) return null;
const dir = this.overrideDirFor(nodeId);
if (!db.isMeshStackEnabled(nodeId, stackName)) {
// Pilot nodes intentionally have no mesh_stacks rows (opt-in state
// lives on central per the C-3 design). Use file-presence as the
// fallback: if central pushed an override via applyLocalOverride, return
// that path so ComposeService picks it up on the next deploy.
const file = path.resolve(dir, `${path.basename(stackName)}.override.yml`);
if (!isPathWithinBase(file, dir)) return null;
try {
await fs.access(file);
return file;
} catch {
return null;
}
}
if (!this.senchoIp) return null;
const aliases: MeshAlias[] = Array.from(this.aliasCache.values()).map((a) => ({ host: a.host }));
const serviceNames = await this.getDeclaredStackServiceNames(stackName, nodeId);
const dir = this.overrideDirFor(nodeId);
await fs.mkdir(dir, { recursive: true });
// path.basename mirrors the applyLocalOverride pattern (and is
// the form CodeQL's path-injection model recognizes).
@@ -705,7 +718,7 @@ export class MeshService extends EventEmitter implements MeshForwarderHost {
private async removeStackOverride(nodeId: number, stackName: string): Promise<void> {
if (!isValidStackName(stackName)) return;
const dir = this.overrideDirFor(nodeId);
const file = path.resolve(dir, `${stackName}.override.yml`);
const file = path.resolve(dir, `${path.basename(stackName)}.override.yml`);
if (!isPathWithinBase(file, dir)) return;
try { await fs.unlink(file); } catch { /* ignore not-exist */ }
}