@kici-dev/orchestrator 0.18.0 → 0.20.0

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.
@@ -70,8 +70,16 @@ export interface AgentEntry {
70
70
  * In memory rather than DB-backed, unlike reboot-pending: the destroy is issued by
71
71
  * this process, lasts seconds, and ends with the WebSocket close that removes the
72
72
  * entry — there is nothing to outlive.
73
+ *
74
+ * Also set when the agent itself reports `draining` on `agent.status` (a SIGUSR1
75
+ * drain, or a single-job agent that accepted its job); see {@link drainReported}.
73
76
  */
74
77
  draining: boolean;
78
+ /**
79
+ * The agent reported `draining` itself. Its drain is terminal for this connection,
80
+ * so {@link AgentRegistry.clearDraining} (the warm-pool undo) leaves it draining.
81
+ */
82
+ drainReported?: boolean;
75
83
  /**
76
84
  * Epoch ms until which the agent is held out of routing after it rejected a
77
85
  * dispatch as busy. Unset when no hold is in force. See {@link AgentRegistry.markBusyHeld}.
@@ -337,8 +345,11 @@ export declare class AgentRegistry {
337
345
  */
338
346
  isRegistered(agentId: string): boolean;
339
347
  /**
340
- * Condemn an agent before a warm-pool destroy: it stays registered and finishes its
341
- * current work, but takes no new dispatches.
348
+ * Condemn an agent: it stays registered and keeps finishing its current work,
349
+ * but takes no new dispatches.
350
+ *
351
+ * Called before a warm-pool destroy is issued. An agent that reports its own
352
+ * drain goes through {@link markDrainReported} instead.
342
353
  *
343
354
  * @returns the agent's `activeJobs`, so the caller can see a job claimed between its
344
355
  * idle check and this mark and back out; `undefined` when not registered
@@ -382,7 +393,15 @@ export declare class AgentRegistry {
382
393
  * free slot now.
383
394
  */
384
395
  clearExpiredBusyHold(agentId: string, now?: number): void;
385
- /** Undo {@link markDraining} — a destroy that was rejected leaves the agent alive. */
396
+ /**
397
+ * The agent reported on `agent.status` that it takes no new job: a drain, or a
398
+ * single-job agent that has accepted its job. Terminal for this connection.
399
+ */
400
+ markDrainReported(agentId: string): void;
401
+ /**
402
+ * Undo {@link markDraining} — a destroy that was rejected leaves the agent alive.
403
+ * An agent that reported its own drain stays draining.
404
+ */
386
405
  clearDraining(agentId: string): void;
387
406
  get(agentId: string): AgentEntry | undefined;
388
407
  getByWs(ws: WsLike): AgentEntry | undefined;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The container-scaler agent image check `kici-admin orchestrator upgrade`
3
+ * runs before it restarts the service.
4
+ *
5
+ * A container scaler spawns each agent from the image its label set names, not
6
+ * from the upgraded orchestrator. So an upgrade leaves container agents on the
7
+ * old release when a label set pins an older `kici-agent:<version>`, or names
8
+ * the moving `:latest` tag with the default `imagePullPolicy: IfNotPresent`: a
9
+ * host that already pulled `:latest` keeps that image. The check only warns:
10
+ * it never rewrites the operator's scaler config and never stops the upgrade.
11
+ */
12
+ import { type ScalerConfig } from '../../../scaler/types.js';
13
+ /** Why a container label set keeps spawning an old agent after the upgrade. */
14
+ export declare enum AgentImageIssue {
15
+ /** `:latest` (or no tag) with `IfNotPresent`: the host keeps the image it pulled. */
16
+ MovingTag = "moving-tag",
17
+ /** A `kici-agent:<version>` older than the release the upgrade installs. */
18
+ OlderPin = "older-pin"
19
+ }
20
+ export interface AgentImageFinding {
21
+ issue: AgentImageIssue;
22
+ scaler: string;
23
+ labels: string[];
24
+ image: string;
25
+ message: string;
26
+ }
27
+ /** An image reference split into its repository, tag and digest. */
28
+ export interface ImageRefParts {
29
+ repository: string;
30
+ tag: string | null;
31
+ digest: string | null;
32
+ }
33
+ /**
34
+ * Split `[registry/]path/name[:tag][@digest]`. A `:` before the last `/` is a
35
+ * registry port, not a tag.
36
+ */
37
+ export declare function parseImageRef(image: string): ImageRefParts;
38
+ export interface CheckAgentImagesArgs {
39
+ config: ScalerConfig;
40
+ /** The release the upgrade installs, or null when the caller does not know it. */
41
+ targetVersion: string | null;
42
+ /** The main scaler config file, for the remedy text. */
43
+ scalerConfigPath: string;
44
+ /** The `KICI_SCALER_CONFIG_DIR` directory, when one is set. */
45
+ scalerConfigDir?: string;
46
+ }
47
+ /**
48
+ * Every container-scaler label set whose agent image an upgrade to
49
+ * `targetVersion` leaves unchanged. An empty result means each agent image is
50
+ * pinned to the target (or newer), pulled on every spawn, or not a KiCI agent.
51
+ */
52
+ export declare function checkContainerAgentImages(args: CheckAgentImagesArgs): AgentImageFinding[];
53
+ export interface WarnAgentImagesArgs {
54
+ envFilePath: string;
55
+ workingDirectory: string;
56
+ /** The release the upgrade installs, or null when the caller does not know it. */
57
+ targetVersion: string | null;
58
+ warn: (line: string) => void;
59
+ }
60
+ /**
61
+ * Print one warning per container label set whose agent image the upgrade
62
+ * leaves unchanged. Never throws, and stays silent when the env file or the
63
+ * scaler config cannot be read: the bare-metal `binaryPath` check, which runs
64
+ * first on the same files, already reports that.
65
+ */
66
+ export declare function warnAboutContainerAgentImages(args: WarnAgentImagesArgs): Promise<void>;
67
+ //# sourceMappingURL=container-agent-image-check.d.ts.map
@@ -265,13 +265,13 @@ export declare function npmReleaseReadsEnvFile(target: NpmInstallTarget, version
265
265
  error: string;
266
266
  };
267
267
  /**
268
- * Install `<owningPackage>@<version>` globally under the unit's pinned node, so
269
- * it lands in the prefix the unit's ExecStart resolves from. npm derives its
270
- * global prefix from the node that runs npm-cli.js, and the unit's
271
- * `<pinned>/bin/npm` (an `env node` shebang or a version-manager wrapper)
272
- * resolves `node` off PATH; run bare it would inherit the shell's active node,
273
- * the mise/nvm/asdf drift this upgrade fixes. Hence the pinned bin dir goes
274
- * first on PATH ({@link npmEnv}).
268
+ * Install `<owningPackage>@<version>` globally into the prefix the unit's
269
+ * ExecStart resolves from, with the unit's pinned node. Without an explicit
270
+ * prefix npm derives one from the node that runs npm-cli.js, or takes the
271
+ * `npm_config_prefix` the caller's environment carries (`npx` exports its
272
+ * own), so the install would land where the unit never reads it. The pinned
273
+ * node runs npm-cli.js directly, skipping a version-manager wrapper at
274
+ * `<pinned>/bin/npm`, which also keeps the shell's active node out of it.
275
275
  */
276
276
  export declare function installGlobalPackage(target: NpmInstallTarget, version: string, run?: SpawnRunner): {
277
277
  ok: boolean;
@@ -57,16 +57,42 @@ export declare function writeInstallManifest(instanceDir: string, fresh: Instanc
57
57
  * Where an npm-source upgrade installs, from a unit's launch spec.
58
58
  * `owningPackage` is the top-level global package ExecStart runs (`kici-admin`
59
59
  * when the component loads through its nested node_modules, else
60
- * `@kici-dev/<component>`). `npmPath` sits beside the pinned node, so
61
- * `npm install -g` lands in the prefix ExecStart resolves from, whatever the
62
- * shell's active node.
60
+ * `@kici-dev/<component>`). `globalPrefix` is the npm prefix the launch entry
61
+ * lives in; every npm call passes it as `--prefix`, because the caller's
62
+ * environment can name another one (`npx` exports `npm_config_prefix`).
63
+ * `npmInvocation` runs `npm-cli.js` with the pinned node when it sits beside
64
+ * that node, so a version-manager wrapper at `npmPath` (mise's reshim needs
65
+ * mise on PATH) is never started.
63
66
  */
64
67
  export interface NpmInstallTarget {
65
68
  nodeExecPath: string;
66
69
  npmPath: string;
67
70
  owningPackage: string;
71
+ globalPrefix: string;
72
+ npmInvocation: {
73
+ cmd: string;
74
+ preArgs: string[];
75
+ };
68
76
  }
77
+ /**
78
+ * The resolution of a launch spec to an install target. `no-entry` covers an
79
+ * opaque `--binary` install and a path outside `node_modules`; `not-global`
80
+ * an entry under a local `node_modules` tree, whose prefix the unit's npm
81
+ * cannot install into with `-g`.
82
+ */
83
+ export type NpmInstallResolution = {
84
+ ok: true;
85
+ target: NpmInstallTarget;
86
+ } | {
87
+ ok: false;
88
+ reason: 'no-entry';
89
+ } | {
90
+ ok: false;
91
+ reason: 'not-global';
92
+ entry: string;
93
+ };
69
94
  export declare function resolveNpmInstallTarget(spec: LaunchSpec, component: Component, opts: {
70
95
  windows: boolean;
71
- }): NpmInstallTarget | null;
96
+ exists?: (p: string) => boolean;
97
+ }): NpmInstallResolution;
72
98
  //# sourceMappingURL=manifest.d.ts.map