the platform can allocate a plugin a port, and remember it
A sidecar already gets a port and nothing here changes that: it binds 0, the
kernel picks, it announces the number. That is right for what it is - a private
detail between two processes, loopback, re-chosen every restart, and nothing
outside Officer needs it.
A port something ELSE connects to is a different thing. A plugin serving a
website has to be reachable from nginx, which means the number must survive a
restart and be knowable by whoever writes the proxy configuration. Ephemeral
fails both, which is why the first site plugin was going to hardcode 3011 and
hope.
So: `extraPorts: [{ name: 'site', preferred: 3011 }]` in a manifest, allocated at
install, persisted in plugin_ports, and delivered as PORT_SITE in the sidecar's
pm2 environment. Environment rather than the registration socket because a socket
answer arrives after the process has already had to decide what to bind, and pm2
is the only thing that can tell a process something before it starts.
What the platform guarantees is the part per-plugin config could never do: no two
plugins are ever handed the same number. That is a unique index rather than a
convention, because a collision is not a duplicate row - it is two services
fighting over a listener where the loser fails to bind and the winner is whoever
started first.
`preferred` is a wish, not a claim. A plugin usually arrives after the thing that
connects to it, and moving that configuration is work the platform can spare
somebody; but a preference that could fail an install would be a claim wearing a
softer word.
Allocations survive uninstall and are released only by purge. The number is
written down where this database cannot see it, so handing it to a different
plugin because this one was briefly removed points that configuration at the
wrong service.
Pm2App gained `env`, emitted as JSON rather than hand-quoted: a port is a number
today, but this is the seam where a path arrives eventually, and one apostrophe
in a value produces a file PM2 cannot parse - which takes every process on the
machine down, not just the plugin's.
Verified: stable across calls, distinct across plugins, released cleanly,
honours a preference, and a plugin declaring no ports gets no env key at all.
This commit is contained in:
@@ -32,7 +32,7 @@ import { CATALOGUE } from '../src/servers/app-store/catalogue';
|
||||
import { PLATFORM_DIR } from '../src/servers/data-path';
|
||||
import { discoverPlugins } from '../src/servers/plugins/discover';
|
||||
import { pluginPm2Entry, type Pm2App } from '../src/servers/plugins/ecosystem';
|
||||
import { listPluginInstalls, listSidecarInstalls } from 'officerdb';
|
||||
import { listPluginInstalls, listPluginPorts, listSidecarInstalls } from 'officerdb';
|
||||
|
||||
const done = (message: string): never => {
|
||||
console.log(`[gen:ecosystem] ${message}`);
|
||||
@@ -92,7 +92,24 @@ const DISABLED = new Set(
|
||||
async function pluginEntries(): Promise<Pm2App[]> {
|
||||
const [installs, { plugins }] = await Promise.all([listPluginInstalls(), discoverPlugins()]);
|
||||
const enabled = new Set(installs.filter((i) => i.enabled).map((i) => i.appName));
|
||||
return plugins.filter((p) => enabled.has(p.appName)).flatMap((p) => pluginPm2Entry(p) ?? []);
|
||||
/*
|
||||
Ports come from the database, not from a fresh allocation.
|
||||
|
||||
This script rebuilds the whole file from what is installed, and it must produce the SAME entries the
|
||||
installer produced — allocating here would hand a plugin a different number than the one already
|
||||
written into somebody's nginx configuration.
|
||||
*/
|
||||
const allocations = await listPluginPorts();
|
||||
const byPlugin = new Map<string, Record<string, number>>();
|
||||
for (const row of allocations) {
|
||||
const current = byPlugin.get(row.appName) ?? {};
|
||||
current[row.name] = row.port;
|
||||
byPlugin.set(row.appName, current);
|
||||
}
|
||||
|
||||
return plugins
|
||||
.filter((p) => enabled.has(p.appName))
|
||||
.flatMap((p) => pluginPm2Entry(p, byPlugin.get(p.appName)) ?? []);
|
||||
}
|
||||
|
||||
/** Installed AND enabled. Unlike a plugin, every catalogue entry has a `process` — none are filtered out. */
|
||||
|
||||
Reference in New Issue
Block a user