@kapeta/local-cluster-service 0.36.1 → 0.38.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.
- package/.vscode/launch.json +9 -5
- package/CHANGELOG.md +21 -0
- package/dist/cjs/src/assetManager.d.ts +2 -1
- package/dist/cjs/src/assetManager.js +7 -4
- package/dist/cjs/src/containerManager.d.ts +6 -3
- package/dist/cjs/src/containerManager.js +96 -18
- package/dist/cjs/src/instanceManager.d.ts +16 -0
- package/dist/cjs/src/instanceManager.js +78 -14
- package/dist/cjs/src/operatorManager.d.ts +3 -4
- package/dist/cjs/src/operatorManager.js +3 -4
- package/dist/cjs/src/progressListener.d.ts +8 -1
- package/dist/cjs/src/progressListener.js +12 -1
- package/dist/cjs/src/repositoryManager.js +3 -2
- package/dist/cjs/src/taskManager.d.ts +2 -0
- package/dist/cjs/src/taskManager.js +9 -0
- package/dist/cjs/src/types.d.ts +4 -19
- package/dist/cjs/src/types.js +5 -1
- package/dist/cjs/src/utils/BlockInstanceRunner.js +16 -5
- package/dist/cjs/src/utils/commandLineUtils.d.ts +2 -1
- package/dist/cjs/src/utils/commandLineUtils.js +7 -1
- package/dist/cjs/src/utils/utils.d.ts +3 -3
- package/dist/cjs/src/utils/utils.js +4 -3
- package/dist/esm/src/assetManager.d.ts +2 -1
- package/dist/esm/src/assetManager.js +7 -4
- package/dist/esm/src/containerManager.d.ts +6 -3
- package/dist/esm/src/containerManager.js +96 -18
- package/dist/esm/src/instanceManager.d.ts +16 -0
- package/dist/esm/src/instanceManager.js +78 -14
- package/dist/esm/src/operatorManager.d.ts +3 -4
- package/dist/esm/src/operatorManager.js +3 -4
- package/dist/esm/src/progressListener.d.ts +8 -1
- package/dist/esm/src/progressListener.js +12 -1
- package/dist/esm/src/repositoryManager.js +3 -2
- package/dist/esm/src/taskManager.d.ts +2 -0
- package/dist/esm/src/taskManager.js +9 -0
- package/dist/esm/src/types.d.ts +4 -19
- package/dist/esm/src/types.js +5 -1
- package/dist/esm/src/utils/BlockInstanceRunner.js +16 -5
- package/dist/esm/src/utils/commandLineUtils.d.ts +2 -1
- package/dist/esm/src/utils/commandLineUtils.js +7 -1
- package/dist/esm/src/utils/utils.d.ts +3 -3
- package/dist/esm/src/utils/utils.js +4 -3
- package/package.json +17 -16
- package/src/assetManager.ts +9 -7
- package/src/containerManager.ts +110 -23
- package/src/instanceManager.ts +87 -16
- package/src/operatorManager.ts +11 -6
- package/src/progressListener.ts +15 -1
- package/src/repositoryManager.ts +5 -3
- package/src/taskManager.ts +11 -0
- package/src/types.ts +5 -19
- package/src/utils/BlockInstanceRunner.ts +21 -8
- package/src/utils/commandLineUtils.ts +10 -2
- package/src/utils/utils.ts +5 -6
package/dist/cjs/src/types.d.ts
CHANGED
@@ -6,6 +6,10 @@ import express from 'express';
|
|
6
6
|
import { Connection, Resource } from '@kapeta/schemas';
|
7
7
|
import { StringBodyRequest } from './middleware/stringBody';
|
8
8
|
import { KapetaRequest } from './middleware/kapeta';
|
9
|
+
export declare const KIND_RESOURCE_OPERATOR = "core/resource-type-operator";
|
10
|
+
export declare const KIND_BLOCK_TYPE = "core/block-type";
|
11
|
+
export declare const KIND_BLOCK_TYPE_OPERATOR = "core/block-type-operator";
|
12
|
+
export declare const KIND_BLOCK_TYPE_EXECUTABLE = "core/block-type-executable";
|
9
13
|
export type StringMap = {
|
10
14
|
[key: string]: string;
|
11
15
|
};
|
@@ -62,25 +66,6 @@ export interface Health {
|
|
62
66
|
timeout?: number;
|
63
67
|
retries?: number;
|
64
68
|
}
|
65
|
-
export type PortInfo = {
|
66
|
-
port: number;
|
67
|
-
type: 'tcp' | 'udp';
|
68
|
-
} | number | string;
|
69
|
-
export type LocalImageOptions<Credentials = AnyMap, Options = AnyMap> = {
|
70
|
-
image: string;
|
71
|
-
ports: {
|
72
|
-
[key: string]: PortInfo;
|
73
|
-
};
|
74
|
-
credentials?: Credentials;
|
75
|
-
options?: Options;
|
76
|
-
cmd?: string;
|
77
|
-
env?: AnyMap;
|
78
|
-
health?: Health;
|
79
|
-
singleton?: boolean;
|
80
|
-
mounts?: {
|
81
|
-
[key: string]: string;
|
82
|
-
};
|
83
|
-
};
|
84
69
|
export type InstanceInfo = {
|
85
70
|
systemId: string;
|
86
71
|
instanceId: string;
|
package/dist/cjs/src/types.js
CHANGED
@@ -4,7 +4,11 @@
|
|
4
4
|
* SPDX-License-Identifier: BUSL-1.1
|
5
5
|
*/
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
7
|
-
exports.DesiredInstanceStatus = exports.InstanceStatus = exports.InstanceOwner = exports.InstanceType = void 0;
|
7
|
+
exports.DesiredInstanceStatus = exports.InstanceStatus = exports.InstanceOwner = exports.InstanceType = exports.KIND_BLOCK_TYPE_EXECUTABLE = exports.KIND_BLOCK_TYPE_OPERATOR = exports.KIND_BLOCK_TYPE = exports.KIND_RESOURCE_OPERATOR = void 0;
|
8
|
+
exports.KIND_RESOURCE_OPERATOR = 'core/resource-type-operator';
|
9
|
+
exports.KIND_BLOCK_TYPE = 'core/block-type';
|
10
|
+
exports.KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
|
11
|
+
exports.KIND_BLOCK_TYPE_EXECUTABLE = 'core/block-type-executable';
|
8
12
|
var InstanceType;
|
9
13
|
(function (InstanceType) {
|
10
14
|
InstanceType["DOCKER"] = "docker";
|
@@ -19,8 +19,8 @@ const clusterService_1 = require("../clusterService");
|
|
19
19
|
const types_1 = require("../types");
|
20
20
|
const definitionsManager_1 = require("../definitionsManager");
|
21
21
|
const node_os_1 = __importDefault(require("node:os"));
|
22
|
+
const node_path_1 = __importDefault(require("node:path"));
|
22
23
|
const taskManager_1 = require("../taskManager");
|
23
|
-
const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
|
24
24
|
const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
|
25
25
|
const KAPETA_BLOCK_REF = 'KAPETA_BLOCK_REF';
|
26
26
|
const KAPETA_INSTANCE_ID = 'KAPETA_INSTANCE_ID';
|
@@ -115,7 +115,7 @@ class BlockInstanceRunner {
|
|
115
115
|
throw new Error(`Kind not found: ${kindUri.id}`);
|
116
116
|
}
|
117
117
|
let processInfo;
|
118
|
-
if (providerVersion.definition.kind === KIND_BLOCK_TYPE_OPERATOR) {
|
118
|
+
if (providerVersion.definition.kind === types_1.KIND_BLOCK_TYPE_OPERATOR) {
|
119
119
|
processInfo = await this._startOperatorProcess(blockInstance, blockUri, providerVersion, env);
|
120
120
|
}
|
121
121
|
else {
|
@@ -145,6 +145,7 @@ class BlockInstanceRunner {
|
|
145
145
|
if (!assetVersion.definition.spec?.target?.kind) {
|
146
146
|
throw new Error('Missing target kind in block definition');
|
147
147
|
}
|
148
|
+
const realLocalPath = await fs_extra_1.default.realpath(baseDir);
|
148
149
|
const kindUri = (0, nodejs_utils_1.parseKapetaUri)(assetVersion.definition.kind);
|
149
150
|
const providerVersion = await getProvider(kindUri);
|
150
151
|
if (!providerVersion) {
|
@@ -159,10 +160,21 @@ class BlockInstanceRunner {
|
|
159
160
|
if (!localContainer) {
|
160
161
|
throw new Error(`Missing local container information from target: ${targetKindUri.id}`);
|
161
162
|
}
|
162
|
-
|
163
|
-
|
163
|
+
let dockerImage = localContainer.image;
|
164
|
+
const isDockerImage = !localContainer.type || localContainer.type.toLowerCase() === 'docker';
|
165
|
+
const isDockerFile = Boolean(localContainer.type && localContainer.type.toLowerCase() === 'dockerfile');
|
166
|
+
if (isDockerImage && !dockerImage) {
|
164
167
|
throw new Error(`Missing docker image information: ${JSON.stringify(localContainer)}`);
|
165
168
|
}
|
169
|
+
if (isDockerFile) {
|
170
|
+
dockerImage = blockInfo.fullName + ':local';
|
171
|
+
const dockerFile = node_path_1.default.join(realLocalPath, localContainer.file ?? 'Dockerfile');
|
172
|
+
if (!fs_extra_1.default.existsSync(dockerFile)) {
|
173
|
+
throw new Error(`Dockerfile not found at: ${dockerFile}`);
|
174
|
+
}
|
175
|
+
const task = containerManager_1.containerManager.buildDockerImage(dockerFile, blockInfo.fullName + ':local');
|
176
|
+
await task.wait();
|
177
|
+
}
|
166
178
|
const containerName = await (0, utils_1.getBlockInstanceContainerName)(this._systemId, blockInstance.id, targetKindUri.id);
|
167
179
|
const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
|
168
180
|
const dockerOpts = localContainer.options ?? {};
|
@@ -181,7 +193,6 @@ class BlockInstanceRunner {
|
|
181
193
|
if (localContainer.healthcheck) {
|
182
194
|
HealthCheck = containerManager_1.containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
|
183
195
|
}
|
184
|
-
const realLocalPath = await fs_extra_1.default.realpath(baseDir);
|
185
196
|
const Mounts = containerManager_1.containerManager.toDockerMounts({
|
186
197
|
[workingDir]: (0, containerManager_1.toLocalBindVolume)(realLocalPath),
|
187
198
|
});
|
@@ -2,6 +2,7 @@
|
|
2
2
|
* Copyright 2023 Kapeta Inc.
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
|
+
import { Task } from '../taskManager';
|
5
6
|
export declare function hasCLI(): Promise<boolean>;
|
6
|
-
export declare function ensureCLI(): Promise<
|
7
|
+
export declare function ensureCLI(): Promise<Task<void> | null>;
|
7
8
|
export declare function ensureCLICommands(): Promise<void>;
|
@@ -15,10 +15,16 @@ async function ensureCLI() {
|
|
15
15
|
if (await hasCLI()) {
|
16
16
|
return null;
|
17
17
|
}
|
18
|
-
return taskManager_1.taskManager.add(`cli:install`, () => {
|
18
|
+
return taskManager_1.taskManager.add(`cli:install`, (task) => {
|
19
19
|
const process = (0, nodejs_process_1.spawn)('npm', ['install', '-g', '@kapeta/kap'], {
|
20
20
|
shell: true,
|
21
21
|
});
|
22
|
+
process.process.stdout?.on('data', (data) => {
|
23
|
+
task.addLog(data.toString(), 'INFO');
|
24
|
+
});
|
25
|
+
process.process.stderr?.on('data', (data) => {
|
26
|
+
task.addLog(data.toString(), 'ERROR');
|
27
|
+
});
|
22
28
|
return process.wait();
|
23
29
|
}, {
|
24
30
|
name: `Installing Kapeta CLI`,
|
@@ -2,10 +2,10 @@
|
|
2
2
|
* Copyright 2023 Kapeta Inc.
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
|
-
import { EntityList } from '@kapeta/schemas';
|
6
|
-
import { AnyMap
|
5
|
+
import { EntityList, LocalInstancePort } from '@kapeta/schemas';
|
6
|
+
import { AnyMap } from '../types';
|
7
7
|
export declare function getBlockInstanceContainerName(systemId: string, instanceId: string, blockType?: string): Promise<string>;
|
8
|
-
export declare function toPortInfo(port:
|
8
|
+
export declare function toPortInfo(port: LocalInstancePort): LocalInstancePort | {
|
9
9
|
port: number;
|
10
10
|
type: string;
|
11
11
|
};
|
@@ -11,11 +11,12 @@ exports.getResolvedConfiguration = exports.getBindHost = exports.isLinux = expor
|
|
11
11
|
const node_fs_1 = __importDefault(require("node:fs"));
|
12
12
|
const yaml_1 = __importDefault(require("yaml"));
|
13
13
|
const md5_1 = __importDefault(require("md5"));
|
14
|
+
const schemas_1 = require("@kapeta/schemas");
|
14
15
|
const lodash_1 = __importDefault(require("lodash"));
|
16
|
+
const types_1 = require("../types");
|
15
17
|
const local_cluster_config_1 = __importDefault(require("@kapeta/local-cluster-config"));
|
16
18
|
const definitionsManager_1 = require("../definitionsManager");
|
17
19
|
const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
18
|
-
const operatorManager_1 = require("../operatorManager");
|
19
20
|
const assetManager_1 = require("../assetManager");
|
20
21
|
async function getBlockInstanceContainerName(systemId, instanceId, blockType) {
|
21
22
|
if (!blockType) {
|
@@ -33,7 +34,7 @@ async function getBlockInstanceContainerName(systemId, instanceId, blockType) {
|
|
33
34
|
if (!typeDefinition) {
|
34
35
|
throw new Error(`Block type ${blockType} not found`);
|
35
36
|
}
|
36
|
-
if ((0, nodejs_utils_1.parseKapetaUri)(typeDefinition.definition.kind).fullName ===
|
37
|
+
if ((0, nodejs_utils_1.parseKapetaUri)(typeDefinition.definition.kind).fullName === types_1.KIND_BLOCK_TYPE_OPERATOR &&
|
37
38
|
typeDefinition.definition.spec?.local?.singleton) {
|
38
39
|
return `kapeta-instance-operator-${(0, md5_1.default)((0, nodejs_utils_1.normalizeKapetaUri)(systemId) + (0, nodejs_utils_1.normalizeKapetaUri)(blockType))}`;
|
39
40
|
}
|
@@ -45,7 +46,7 @@ function toPortInfo(port) {
|
|
45
46
|
return { port: parseInt(`${port}`), type: 'tcp' };
|
46
47
|
}
|
47
48
|
if (!port.type) {
|
48
|
-
port.type =
|
49
|
+
port.type = schemas_1.LocalInstancePortType.TCP;
|
49
50
|
}
|
50
51
|
return port;
|
51
52
|
}
|
@@ -4,6 +4,7 @@
|
|
4
4
|
*/
|
5
5
|
import { Definition } from '@kapeta/local-cluster-config';
|
6
6
|
import { BlockDefinition, BlockInstance, Plan } from '@kapeta/schemas';
|
7
|
+
import { Task } from './taskManager';
|
7
8
|
import { SourceOfChange } from './types';
|
8
9
|
export interface EnrichedAsset {
|
9
10
|
ref: string;
|
@@ -31,7 +32,7 @@ declare class AssetManager {
|
|
31
32
|
updateAsset(ref: string, yaml: Definition, sourceOfChange?: SourceOfChange): Promise<void>;
|
32
33
|
importFile(filePath: string): Promise<EnrichedAsset[]>;
|
33
34
|
unregisterAsset(ref: string): Promise<void>;
|
34
|
-
installAsset(ref: string, wait?: boolean): Promise<
|
35
|
+
installAsset(ref: string, wait?: boolean): Promise<Task<void>[] | undefined>;
|
35
36
|
private cleanupUnusedProviders;
|
36
37
|
private upgradeAllProviders;
|
37
38
|
private maybeGenerateCode;
|
@@ -18,6 +18,7 @@ const repositoryManager_1 = require("./repositoryManager");
|
|
18
18
|
const nodejs_registry_utils_1 = require("@kapeta/nodejs-registry-utils");
|
19
19
|
const definitionsManager_1 = require("./definitionsManager");
|
20
20
|
const taskManager_1 = require("./taskManager");
|
21
|
+
const types_1 = require("./types");
|
21
22
|
const cacheManager_1 = require("./cacheManager");
|
22
23
|
const node_uuid_1 = __importDefault(require("node-uuid"));
|
23
24
|
const node_os_1 = __importDefault(require("node:os"));
|
@@ -75,8 +76,9 @@ class AssetManager {
|
|
75
76
|
async getAssets(assetKinds) {
|
76
77
|
if (!assetKinds) {
|
77
78
|
const blockTypeProviders = await definitionsManager_1.definitionsManager.getDefinitions([
|
78
|
-
|
79
|
-
|
79
|
+
types_1.KIND_BLOCK_TYPE,
|
80
|
+
types_1.KIND_BLOCK_TYPE_OPERATOR,
|
81
|
+
types_1.KIND_BLOCK_TYPE_EXECUTABLE,
|
80
82
|
]);
|
81
83
|
assetKinds = blockTypeProviders.map((p) => {
|
82
84
|
return `${p.definition.metadata.name}:${p.version}`;
|
@@ -231,11 +233,12 @@ class AssetManager {
|
|
231
233
|
return;
|
232
234
|
}
|
233
235
|
console.log('Installing updates', refs);
|
234
|
-
const updateAll = async () => {
|
236
|
+
const updateAll = async (task) => {
|
237
|
+
const progressListener = new progressListener_1.TaskProgressListener(task);
|
235
238
|
try {
|
236
239
|
//We change to a temp dir to avoid issues with the current working directory
|
237
240
|
process.chdir(node_os_1.default.tmpdir());
|
238
|
-
await nodejs_registry_utils_1.Actions.install(
|
241
|
+
await nodejs_registry_utils_1.Actions.install(progressListener, refs, {});
|
239
242
|
await this.cleanupUnusedProviders();
|
240
243
|
}
|
241
244
|
catch (e) {
|
@@ -5,7 +5,9 @@
|
|
5
5
|
/// <reference types="node" />
|
6
6
|
import FSExtra from 'fs-extra';
|
7
7
|
import Docker from 'dockerode';
|
8
|
-
import {
|
8
|
+
import { InstanceInfo, LogEntry } from './types';
|
9
|
+
import { Task } from './taskManager';
|
10
|
+
import { LocalInstanceHealth } from '@kapeta/schemas';
|
9
11
|
type StringMap = {
|
10
12
|
[key: string]: string;
|
11
13
|
};
|
@@ -50,11 +52,11 @@ declare class ContainerManager {
|
|
50
52
|
getContainerByName(containerName: string): Promise<ContainerInfo | undefined>;
|
51
53
|
pull(image: string): Promise<boolean>;
|
52
54
|
toDockerMounts(mounts: StringMap): DockerMounts[];
|
53
|
-
toDockerHealth(health:
|
55
|
+
toDockerHealth(health: LocalInstanceHealth): {
|
54
56
|
Test: string[];
|
55
57
|
Interval: number;
|
56
58
|
Timeout: number;
|
57
|
-
Retries:
|
59
|
+
Retries: any;
|
58
60
|
};
|
59
61
|
private applyHash;
|
60
62
|
ensureContainer(opts: any): Promise<Docker.Container>;
|
@@ -74,6 +76,7 @@ declare class ContainerManager {
|
|
74
76
|
getLogs(instance: InstanceInfo): Promise<LogEntry[]>;
|
75
77
|
stopLogListening(systemId: string, instanceId: string): Promise<void>;
|
76
78
|
ensureLogListening(systemId: string, instanceId: string, handler: (log: LogEntry) => void): Promise<void>;
|
79
|
+
buildDockerImage(dockerFile: string, imageName: string): Task<void>;
|
77
80
|
}
|
78
81
|
declare class ClosableLogStream {
|
79
82
|
private readonly stream;
|
@@ -207,14 +207,8 @@ class ContainerManager {
|
|
207
207
|
return this._docker;
|
208
208
|
}
|
209
209
|
async getContainerByName(containerName) {
|
210
|
-
|
211
|
-
|
212
|
-
return container.Names.indexOf(`/${containerName}`) > -1;
|
213
|
-
});
|
214
|
-
if (out) {
|
215
|
-
return this.get(out.Id);
|
216
|
-
}
|
217
|
-
return undefined;
|
210
|
+
// The container can be fetched by name or by id using the same API call
|
211
|
+
return this.get(containerName);
|
218
212
|
}
|
219
213
|
async pull(image) {
|
220
214
|
let [imageName, tag] = image.split(/:/);
|
@@ -279,6 +273,10 @@ class ContainerManager {
|
|
279
273
|
};
|
280
274
|
}
|
281
275
|
const chunk = chunks[data.id];
|
276
|
+
if (data.stream) {
|
277
|
+
// Emit raw output to the task log
|
278
|
+
task.addLog(data.stream);
|
279
|
+
}
|
282
280
|
switch (data.status) {
|
283
281
|
case DockerPullEventTypes.PreparingPhase:
|
284
282
|
case DockerPullEventTypes.WaitingPhase:
|
@@ -520,7 +518,7 @@ class ContainerManager {
|
|
520
518
|
async get(name) {
|
521
519
|
let dockerContainer = null;
|
522
520
|
try {
|
523
|
-
dockerContainer =
|
521
|
+
dockerContainer = this.docker().getContainer(name);
|
524
522
|
await dockerContainer.stats();
|
525
523
|
}
|
526
524
|
catch (err) {
|
@@ -545,7 +543,7 @@ class ContainerManager {
|
|
545
543
|
},
|
546
544
|
];
|
547
545
|
}
|
548
|
-
return containerInfo.getLogs();
|
546
|
+
return await containerInfo.getLogs();
|
549
547
|
}
|
550
548
|
async stopLogListening(systemId, instanceId) {
|
551
549
|
const containerName = await (0, utils_1.getBlockInstanceContainerName)(systemId, instanceId);
|
@@ -616,6 +614,28 @@ class ContainerManager {
|
|
616
614
|
// Ignore
|
617
615
|
}
|
618
616
|
}
|
617
|
+
buildDockerImage(dockerFile, imageName) {
|
618
|
+
const taskName = `Building docker image: ${imageName}`;
|
619
|
+
const processor = async (task) => {
|
620
|
+
const timeStarted = Date.now();
|
621
|
+
const stream = await this.docker().buildImage({
|
622
|
+
context: path_1.default.dirname(dockerFile),
|
623
|
+
src: [path_1.default.basename(dockerFile)],
|
624
|
+
}, {
|
625
|
+
t: imageName,
|
626
|
+
dockerfile: path_1.default.basename(dockerFile),
|
627
|
+
});
|
628
|
+
await processJsonStream(`image:build:${imageName}`, stream, (data) => {
|
629
|
+
if (data.stream) {
|
630
|
+
// Emit raw output to the task log
|
631
|
+
task.addLog(data.stream);
|
632
|
+
}
|
633
|
+
});
|
634
|
+
};
|
635
|
+
return taskManager_1.taskManager.add(`docker:image:build:${imageName}`, processor, {
|
636
|
+
name: taskName,
|
637
|
+
});
|
638
|
+
}
|
619
639
|
}
|
620
640
|
function readLogBuffer(logBuffer) {
|
621
641
|
const out = [];
|
@@ -830,15 +850,73 @@ class ContainerInfo {
|
|
830
850
|
timestamps: true,
|
831
851
|
});
|
832
852
|
const out = readLogBuffer(logs);
|
833
|
-
if (out.length
|
834
|
-
out
|
835
|
-
time: Date.now(),
|
836
|
-
message: 'No logs found for container',
|
837
|
-
level: 'INFO',
|
838
|
-
source: 'stdout',
|
839
|
-
});
|
853
|
+
if (out.length > 0) {
|
854
|
+
return out;
|
840
855
|
}
|
841
|
-
|
856
|
+
const status = await this.status();
|
857
|
+
const healthLogs = status?.Health?.Log
|
858
|
+
? status?.Health?.Log.map((log) => {
|
859
|
+
return {
|
860
|
+
source: 'stdout',
|
861
|
+
level: log.ExitCode === 0 ? 'INFO' : 'ERROR',
|
862
|
+
time: Date.now(),
|
863
|
+
message: 'Health check: ' + log.Output,
|
864
|
+
};
|
865
|
+
})
|
866
|
+
: [];
|
867
|
+
if (status?.Running) {
|
868
|
+
return [
|
869
|
+
{
|
870
|
+
source: 'stdout',
|
871
|
+
level: 'INFO',
|
872
|
+
time: Date.now(),
|
873
|
+
message: 'Container is starting...',
|
874
|
+
},
|
875
|
+
...healthLogs,
|
876
|
+
];
|
877
|
+
}
|
878
|
+
if (status?.Restarting) {
|
879
|
+
return [
|
880
|
+
{
|
881
|
+
source: 'stdout',
|
882
|
+
level: 'INFO',
|
883
|
+
time: Date.now(),
|
884
|
+
message: 'Container is restarting...',
|
885
|
+
},
|
886
|
+
...healthLogs,
|
887
|
+
];
|
888
|
+
}
|
889
|
+
if (status?.Paused) {
|
890
|
+
return [
|
891
|
+
{
|
892
|
+
source: 'stdout',
|
893
|
+
level: 'INFO',
|
894
|
+
time: Date.now(),
|
895
|
+
message: 'Container is paused...',
|
896
|
+
},
|
897
|
+
...healthLogs,
|
898
|
+
];
|
899
|
+
}
|
900
|
+
if (status?.Error) {
|
901
|
+
return [
|
902
|
+
{
|
903
|
+
source: 'stderr',
|
904
|
+
level: 'ERROR',
|
905
|
+
time: Date.now(),
|
906
|
+
message: 'Container failed to start:\n' + status.Error,
|
907
|
+
},
|
908
|
+
...healthLogs,
|
909
|
+
];
|
910
|
+
}
|
911
|
+
return [
|
912
|
+
{
|
913
|
+
source: 'stdout',
|
914
|
+
level: 'INFO',
|
915
|
+
time: Date.now(),
|
916
|
+
message: 'Container not running',
|
917
|
+
...healthLogs,
|
918
|
+
},
|
919
|
+
];
|
842
920
|
}
|
843
921
|
}
|
844
922
|
exports.ContainerInfo = ContainerInfo;
|
@@ -45,7 +45,23 @@ export declare class InstanceManager {
|
|
45
45
|
private requestInstanceStatus;
|
46
46
|
private isSingletonOperator;
|
47
47
|
private getKindForAssetRef;
|
48
|
+
/**
|
49
|
+
* Get the kind of an asset. Use the maxDepth parameter to specify how deep to look for the
|
50
|
+
* kind. For example, if maxDepth is 2, the method will look for the kind of the asset and then
|
51
|
+
* the kind of the kind.
|
52
|
+
* @param assetRef The asset reference
|
53
|
+
* @param maxDepth The maximum depth to look for the kind
|
54
|
+
* @returns The kind of the asset or null if not found
|
55
|
+
*/
|
56
|
+
private getDeepKindForAssetRef;
|
48
57
|
private isUsingKind;
|
49
58
|
private getAllInstancesForKind;
|
59
|
+
/**
|
60
|
+
* Get the ids for all block instances except the ones of the specified kind
|
61
|
+
* @param systemId The plan reference id
|
62
|
+
* @param kind The kind to exclude. Can be a string or an array of strings
|
63
|
+
* @returns An array of block instance ids
|
64
|
+
*/
|
65
|
+
private getAllInstancesExceptKind;
|
50
66
|
}
|
51
67
|
export declare const instanceManager: InstanceManager;
|
@@ -212,7 +212,9 @@ class InstanceManager {
|
|
212
212
|
systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
|
213
213
|
const instance = lodash_1.default.find(this._instances, { systemId, instanceId });
|
214
214
|
if (instance && instance.owner === types_1.InstanceOwner.EXTERNAL && instance.status !== types_1.InstanceStatus.STOPPED) {
|
215
|
-
instance.status
|
215
|
+
if (instance.status != types_1.InstanceStatus.FAILED) {
|
216
|
+
instance.status = types_1.InstanceStatus.STOPPED;
|
217
|
+
}
|
216
218
|
instance.pid = null;
|
217
219
|
instance.health = null;
|
218
220
|
socketManager_1.socketManager.emitSystemEvent(systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
|
@@ -230,11 +232,12 @@ class InstanceManager {
|
|
230
232
|
throw new Error(`No blocks found in plan: ${systemId}`);
|
231
233
|
}
|
232
234
|
return taskManager_1.taskManager.add(`plan:start:${systemId}`, async () => {
|
233
|
-
|
234
|
-
|
235
|
-
|
235
|
+
const promises = [];
|
236
|
+
const errors = [];
|
237
|
+
const instanceIds = await this.getAllInstancesExceptKind(systemId, types_1.KIND_BLOCK_TYPE_EXECUTABLE);
|
238
|
+
for (const instanceId of instanceIds) {
|
236
239
|
try {
|
237
|
-
promises.push(this.start(systemId,
|
240
|
+
promises.push(this.start(systemId, instanceId).then((taskOrInstance) => {
|
238
241
|
if (taskOrInstance instanceof taskManager_1.Task) {
|
239
242
|
return taskOrInstance.wait();
|
240
243
|
}
|
@@ -449,7 +452,7 @@ class InstanceManager {
|
|
449
452
|
// Definition not found
|
450
453
|
return Promise.resolve();
|
451
454
|
}
|
452
|
-
if (
|
455
|
+
if (types_1.KIND_RESOURCE_OPERATOR.toLowerCase() !== asset.definition.kind.toLowerCase()) {
|
453
456
|
// Not an operator
|
454
457
|
return Promise.resolve();
|
455
458
|
}
|
@@ -500,8 +503,7 @@ class InstanceManager {
|
|
500
503
|
];
|
501
504
|
const out = await this.saveInternalInstance({
|
502
505
|
...instance,
|
503
|
-
type: types_1.InstanceType.
|
504
|
-
pid: null,
|
506
|
+
type: types_1.InstanceType.DOCKER,
|
505
507
|
health: null,
|
506
508
|
portType: DEFAULT_HEALTH_PORT_TYPE,
|
507
509
|
status: types_1.InstanceStatus.FAILED,
|
@@ -621,9 +623,8 @@ class InstanceManager {
|
|
621
623
|
}
|
622
624
|
if (instance.status !== newStatus) {
|
623
625
|
const oldStatus = instance.status;
|
624
|
-
const skipUpdate = (
|
625
|
-
|
626
|
-
instance.status === types_1.InstanceStatus.STOPPING) ||
|
626
|
+
const skipUpdate = ([types_1.InstanceStatus.READY, types_1.InstanceStatus.UNHEALTHY].includes(newStatus) &&
|
627
|
+
instance.status === types_1.InstanceStatus.STOPPING) ||
|
627
628
|
(newStatus === types_1.InstanceStatus.STOPPED &&
|
628
629
|
instance.status === types_1.InstanceStatus.STARTING &&
|
629
630
|
instance.desiredStatus === types_1.DesiredInstanceStatus.RUN);
|
@@ -636,7 +637,7 @@ class InstanceManager {
|
|
636
637
|
}
|
637
638
|
}
|
638
639
|
if (instance.desiredStatus === types_1.DesiredInstanceStatus.RUN &&
|
639
|
-
[types_1.InstanceStatus.STOPPED, types_1.InstanceStatus.
|
640
|
+
[types_1.InstanceStatus.STOPPED, types_1.InstanceStatus.STOPPING].includes(newStatus)) {
|
640
641
|
//If the instance is stopped but we want it to run, start it
|
641
642
|
try {
|
642
643
|
await this.start(instance.systemId, instance.instanceId);
|
@@ -707,10 +708,17 @@ class InstanceManager {
|
|
707
708
|
return types_1.InstanceStatus.READY;
|
708
709
|
}
|
709
710
|
if (statusType === 'created') {
|
711
|
+
if (state.ExitCode !== 0) {
|
712
|
+
// Failed during creation
|
713
|
+
return types_1.InstanceStatus.FAILED;
|
714
|
+
}
|
710
715
|
return types_1.InstanceStatus.STARTING;
|
711
716
|
}
|
712
717
|
if (statusType === 'exited' || statusType === 'dead') {
|
713
|
-
|
718
|
+
if (state.ExitCode === 0) {
|
719
|
+
return types_1.InstanceStatus.STOPPED;
|
720
|
+
}
|
721
|
+
return types_1.InstanceStatus.FAILED;
|
714
722
|
}
|
715
723
|
if (statusType === 'removing') {
|
716
724
|
return types_1.InstanceStatus.BUSY;
|
@@ -776,7 +784,7 @@ class InstanceManager {
|
|
776
784
|
if (!provider) {
|
777
785
|
return false;
|
778
786
|
}
|
779
|
-
if ((0, nodejs_utils_1.parseKapetaUri)(provider.kind).fullName ===
|
787
|
+
if ((0, nodejs_utils_1.parseKapetaUri)(provider.kind).fullName === types_1.KIND_BLOCK_TYPE_OPERATOR) {
|
780
788
|
const localConfig = provider.data.spec.local;
|
781
789
|
return localConfig.singleton ?? false;
|
782
790
|
}
|
@@ -789,6 +797,36 @@ class InstanceManager {
|
|
789
797
|
}
|
790
798
|
return block.data.kind;
|
791
799
|
}
|
800
|
+
/**
|
801
|
+
* Get the kind of an asset. Use the maxDepth parameter to specify how deep to look for the
|
802
|
+
* kind. For example, if maxDepth is 2, the method will look for the kind of the asset and then
|
803
|
+
* the kind of the kind.
|
804
|
+
* @param assetRef The asset reference
|
805
|
+
* @param maxDepth The maximum depth to look for the kind
|
806
|
+
* @returns The kind of the asset or null if not found
|
807
|
+
*/
|
808
|
+
async getDeepKindForAssetRef(assetRef, maxDepth) {
|
809
|
+
if (maxDepth <= 0) {
|
810
|
+
return null;
|
811
|
+
}
|
812
|
+
try {
|
813
|
+
const asset = await assetManager_1.assetManager.getAsset(assetRef);
|
814
|
+
if (!asset || !asset.data.kind) {
|
815
|
+
return null;
|
816
|
+
}
|
817
|
+
if (maxDepth === 1) {
|
818
|
+
return asset.data.kind;
|
819
|
+
}
|
820
|
+
else {
|
821
|
+
// Recurse with the kind of the current block and one less depth
|
822
|
+
return await this.getDeepKindForAssetRef(asset.data.kind, maxDepth - 1);
|
823
|
+
}
|
824
|
+
}
|
825
|
+
catch (error) {
|
826
|
+
console.error('Error fetching kind for assetRef:', assetRef, error);
|
827
|
+
return null;
|
828
|
+
}
|
829
|
+
}
|
792
830
|
async isUsingKind(ref, kind) {
|
793
831
|
const assetKind = await this.getKindForAssetRef(ref);
|
794
832
|
if (!assetKind) {
|
@@ -809,6 +847,32 @@ class InstanceManager {
|
|
809
847
|
}
|
810
848
|
return out;
|
811
849
|
}
|
850
|
+
/**
|
851
|
+
* Get the ids for all block instances except the ones of the specified kind
|
852
|
+
* @param systemId The plan reference id
|
853
|
+
* @param kind The kind to exclude. Can be a string or an array of strings
|
854
|
+
* @returns An array of block instance ids
|
855
|
+
*/
|
856
|
+
async getAllInstancesExceptKind(systemId, kind) {
|
857
|
+
const plan = await assetManager_1.assetManager.getPlan(systemId);
|
858
|
+
if (!plan?.spec?.blocks) {
|
859
|
+
return [];
|
860
|
+
}
|
861
|
+
const out = [];
|
862
|
+
const excludedKinds = kind instanceof Array ? kind : [kind];
|
863
|
+
for (const block of plan.spec.blocks) {
|
864
|
+
const blockKindOfKind = await this.getDeepKindForAssetRef(block.block.ref, 2);
|
865
|
+
if (!blockKindOfKind) {
|
866
|
+
continue;
|
867
|
+
}
|
868
|
+
const shouldIncludeBlock = excludedKinds.some((excludedKind) => excludedKind === (0, nodejs_utils_1.parseKapetaUri)(blockKindOfKind).fullName) ===
|
869
|
+
false;
|
870
|
+
if (shouldIncludeBlock) {
|
871
|
+
out.push(block.id);
|
872
|
+
}
|
873
|
+
}
|
874
|
+
return out;
|
875
|
+
}
|
812
876
|
}
|
813
877
|
exports.InstanceManager = InstanceManager;
|
814
878
|
exports.instanceManager = new InstanceManager();
|
@@ -4,13 +4,12 @@
|
|
4
4
|
*/
|
5
5
|
import { DefinitionInfo } from '@kapeta/local-cluster-config';
|
6
6
|
import { ContainerInfo } from './containerManager';
|
7
|
-
import { EnvironmentType,
|
8
|
-
|
9
|
-
export declare const KIND_BLOCK_OPERATOR = "core/block-type-operator";
|
7
|
+
import { EnvironmentType, OperatorInfo } from './types';
|
8
|
+
import { LocalInstance } from '@kapeta/schemas';
|
10
9
|
declare class Operator {
|
11
10
|
private readonly _data;
|
12
11
|
constructor(data: DefinitionInfo);
|
13
|
-
getLocalData():
|
12
|
+
getLocalData(): LocalInstance;
|
14
13
|
getDefinitionInfo(): DefinitionInfo;
|
15
14
|
getCredentials(): any;
|
16
15
|
}
|