@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
@@ -7,21 +7,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
7
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
8
8
|
};
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
10
|
-
exports.operatorManager =
|
10
|
+
exports.operatorManager = void 0;
|
11
11
|
const path_1 = __importDefault(require("path"));
|
12
12
|
const md5_1 = __importDefault(require("md5"));
|
13
13
|
const serviceManager_1 = require("./serviceManager");
|
14
14
|
const storageService_1 = require("./storageService");
|
15
15
|
const containerManager_1 = require("./containerManager");
|
16
16
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
17
|
+
const types_1 = require("./types");
|
17
18
|
const definitionsManager_1 = require("./definitionsManager");
|
18
19
|
const utils_1 = require("./utils/utils");
|
19
20
|
const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
20
21
|
const lodash_1 = __importDefault(require("lodash"));
|
21
22
|
const async_lock_1 = __importDefault(require("async-lock"));
|
22
23
|
const taskManager_1 = require("./taskManager");
|
23
|
-
exports.KIND_RESOURCE_OPERATOR = 'core/resource-type-operator';
|
24
|
-
exports.KIND_BLOCK_OPERATOR = 'core/block-type-operator';
|
25
24
|
const KIND_PLAN = 'core/plan';
|
26
25
|
class Operator {
|
27
26
|
_data;
|
@@ -52,7 +51,7 @@ class OperatorManager {
|
|
52
51
|
* Get operator definition for resource type
|
53
52
|
*/
|
54
53
|
async getOperator(fullName, version) {
|
55
|
-
const operators = await definitionsManager_1.definitionsManager.getDefinitions([
|
54
|
+
const operators = await definitionsManager_1.definitionsManager.getDefinitions([types_1.KIND_RESOURCE_OPERATOR, types_1.KIND_BLOCK_TYPE_OPERATOR]);
|
56
55
|
const operator = operators.find((operator) => operator.definition &&
|
57
56
|
operator.definition.metadata &&
|
58
57
|
operator.definition.metadata.name &&
|
@@ -3,11 +3,13 @@
|
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
5
|
/// <reference types="node" />
|
6
|
+
import { LogEntry } from './types';
|
7
|
+
import { Task } from './taskManager';
|
6
8
|
export declare class ProgressListener {
|
7
9
|
private readonly systemId;
|
8
10
|
private readonly instanceId;
|
9
11
|
constructor(systemId?: string, instanceId?: string);
|
10
|
-
|
12
|
+
protected emitLog(payload: Omit<LogEntry, 'time' | 'source'>): void;
|
11
13
|
run(command: string, directory?: string): Promise<{
|
12
14
|
exit: number;
|
13
15
|
signal: NodeJS.Signals | null;
|
@@ -22,3 +24,8 @@ export declare class ProgressListener {
|
|
22
24
|
info(msg: string, ...args: any[]): void;
|
23
25
|
debug(msg: string, ...args: any[]): void;
|
24
26
|
}
|
27
|
+
export declare class TaskProgressListener extends ProgressListener {
|
28
|
+
private readonly task;
|
29
|
+
constructor(task: Task);
|
30
|
+
protected emitLog(payload: Omit<LogEntry, 'time' | 'source'>): void;
|
31
|
+
}
|
@@ -4,7 +4,7 @@
|
|
4
4
|
* SPDX-License-Identifier: BUSL-1.1
|
5
5
|
*/
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
7
|
-
exports.ProgressListener = void 0;
|
7
|
+
exports.TaskProgressListener = exports.ProgressListener = void 0;
|
8
8
|
const nodejs_process_1 = require("@kapeta/nodejs-process");
|
9
9
|
const socketManager_1 = require("./socketManager");
|
10
10
|
const node_util_1 = require("node:util");
|
@@ -120,3 +120,14 @@ class ProgressListener {
|
|
120
120
|
}
|
121
121
|
}
|
122
122
|
exports.ProgressListener = ProgressListener;
|
123
|
+
class TaskProgressListener extends ProgressListener {
|
124
|
+
task;
|
125
|
+
constructor(task) {
|
126
|
+
super();
|
127
|
+
this.task = task;
|
128
|
+
}
|
129
|
+
emitLog(payload) {
|
130
|
+
this.task.addLog(payload.message, payload.level);
|
131
|
+
}
|
132
|
+
}
|
133
|
+
exports.TaskProgressListener = TaskProgressListener;
|
@@ -164,16 +164,17 @@ class RepositoryManager extends node_events_1.EventEmitter {
|
|
164
164
|
async scheduleInstallation(refs) {
|
165
165
|
//We make sure to only install one asset at a time - otherwise unexpected things might happen
|
166
166
|
const createInstaller = (ref) => {
|
167
|
-
return async () => {
|
167
|
+
return async (task) => {
|
168
168
|
if (await definitionsManager_1.definitionsManager.exists(ref)) {
|
169
169
|
return;
|
170
170
|
}
|
171
|
+
const progressListener = new progressListener_1.TaskProgressListener(task);
|
171
172
|
//console.log(`Installing asset: ${ref}`);
|
172
173
|
//Auto-install missing asset
|
173
174
|
try {
|
174
175
|
//We change to a temp dir to avoid issues with the current working directory
|
175
176
|
process.chdir(node_os_1.default.tmpdir());
|
176
|
-
await nodejs_registry_utils_1.Actions.install(
|
177
|
+
await nodejs_registry_utils_1.Actions.install(progressListener, [ref], {});
|
177
178
|
}
|
178
179
|
catch (e) {
|
179
180
|
console.error(`Failed to install asset: ${ref}`, e);
|
@@ -2,6 +2,7 @@
|
|
2
2
|
* Copyright 2023 Kapeta Inc.
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
|
+
import { LogLevel } from './types';
|
5
6
|
export type TaskRunner<T> = (task: Task<T>) => Promise<T>;
|
6
7
|
export declare enum TaskStatus {
|
7
8
|
PENDING = "PENDING",
|
@@ -44,6 +45,7 @@ export declare class Task<T = void> implements TaskData<T> {
|
|
44
45
|
set errorMessage(errorMessage: string | undefined);
|
45
46
|
set metadata(metadata: TaskMetadata);
|
46
47
|
emitUpdate(): void;
|
48
|
+
addLog(log: string, level?: LogLevel): void;
|
47
49
|
wait(): Promise<T>;
|
48
50
|
toData(): {
|
49
51
|
id: string;
|
@@ -12,6 +12,7 @@ const socketManager_1 = require("./socketManager");
|
|
12
12
|
const EVENT_TASK_UPDATED = 'task-updated';
|
13
13
|
const EVENT_TASK_ADDED = 'task-added';
|
14
14
|
const EVENT_TASK_REMOVED = 'task-removed';
|
15
|
+
const EVENT_TASK_LOG = 'task-log';
|
15
16
|
var TaskStatus;
|
16
17
|
(function (TaskStatus) {
|
17
18
|
TaskStatus["PENDING"] = "PENDING";
|
@@ -54,6 +55,14 @@ class Task {
|
|
54
55
|
emitUpdate() {
|
55
56
|
socketManager_1.socketManager.emitGlobal(EVENT_TASK_UPDATED, this.toData());
|
56
57
|
}
|
58
|
+
addLog(log, level = 'INFO') {
|
59
|
+
socketManager_1.socketManager.emitGlobal(EVENT_TASK_LOG, {
|
60
|
+
id: this.id,
|
61
|
+
message: log,
|
62
|
+
level,
|
63
|
+
time: Date.now(),
|
64
|
+
});
|
65
|
+
}
|
57
66
|
async wait() {
|
58
67
|
return this.future.promise;
|
59
68
|
}
|
package/dist/esm/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/esm/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
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@kapeta/local-cluster-service",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.38.0",
|
4
4
|
"description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
|
5
5
|
"type": "commonjs",
|
6
6
|
"exports": {
|
@@ -35,7 +35,8 @@
|
|
35
35
|
},
|
36
36
|
"scripts": {
|
37
37
|
"start": "node ./dist/cjs/start.js",
|
38
|
-
"dev": "
|
38
|
+
"dev": "npm run start:dev",
|
39
|
+
"start:dev": "nodemon -e js,ts,json ./start.ts",
|
39
40
|
"test": "jest",
|
40
41
|
"clean": "rm -rf ./dist",
|
41
42
|
"build:esm": "tsc --module nodenext --moduleResolution nodenext --outDir ./dist/esm && echo '{\"type\":\"module\"}' > ./dist/esm/package.json",
|
@@ -47,19 +48,19 @@
|
|
47
48
|
},
|
48
49
|
"homepage": "https://github.com/kapetacom/local-cluster-service#readme",
|
49
50
|
"dependencies": {
|
50
|
-
"@kapeta/codegen": "^1.
|
51
|
-
"@kapeta/kaplang-core": "^1.
|
52
|
-
"@kapeta/local-cluster-config": "
|
53
|
-
"@kapeta/nodejs-api-client": ">=0.
|
54
|
-
"@kapeta/nodejs-process": "
|
55
|
-
"@kapeta/nodejs-registry-utils": ">=0.
|
51
|
+
"@kapeta/codegen": "^1.3.0",
|
52
|
+
"@kapeta/kaplang-core": "^1.11.2",
|
53
|
+
"@kapeta/local-cluster-config": "^0.4.0",
|
54
|
+
"@kapeta/nodejs-api-client": ">=0.2.0 <2",
|
55
|
+
"@kapeta/nodejs-process": "^1.2.0",
|
56
|
+
"@kapeta/nodejs-registry-utils": ">=0.10.0 <2",
|
56
57
|
"@kapeta/nodejs-utils": "<2",
|
57
|
-
"@kapeta/schemas": "^
|
58
|
-
"@kapeta/sdk-config": "^2.
|
59
|
-
"@kapeta/ui-web-components": "^3.0
|
60
|
-
"@kapeta/ui-web-plan-editor": "^2.
|
61
|
-
"@kapeta/ui-web-types": "^1.
|
62
|
-
"@kapeta/web-microfrontend": "^1",
|
58
|
+
"@kapeta/schemas": "^3.4.0",
|
59
|
+
"@kapeta/sdk-config": "^2.1.0",
|
60
|
+
"@kapeta/ui-web-components": "^3.1.0",
|
61
|
+
"@kapeta/ui-web-plan-editor": "^2.3.1",
|
62
|
+
"@kapeta/ui-web-types": "^1.3.1",
|
63
|
+
"@kapeta/web-microfrontend": "^1.2.5",
|
63
64
|
"@sentry/node": "^7.94.1",
|
64
65
|
"@types/dockerode": "^3.3.19",
|
65
66
|
"@types/stream-json": "^1.7.3",
|
@@ -85,8 +86,8 @@
|
|
85
86
|
"yaml": "^1.6.0"
|
86
87
|
},
|
87
88
|
"devDependencies": {
|
88
|
-
"@kapeta/eslint-config": "^0.
|
89
|
-
"@kapeta/prettier-config": "^0.6.
|
89
|
+
"@kapeta/eslint-config": "^0.7.0",
|
90
|
+
"@kapeta/prettier-config": "^0.6.2",
|
90
91
|
"@tsconfig/node18": "^18.2.0",
|
91
92
|
"@types/async-lock": "^1.4.0",
|
92
93
|
"@types/express": "^4.17.17",
|
package/src/assetManager.ts
CHANGED
@@ -8,14 +8,14 @@ import FS from 'fs-extra';
|
|
8
8
|
import YAML from 'yaml';
|
9
9
|
import { Definition, DefinitionInfo } from '@kapeta/local-cluster-config';
|
10
10
|
import { codeGeneratorManager } from './codeGeneratorManager';
|
11
|
-
import { ProgressListener } from './progressListener';
|
11
|
+
import { ProgressListener, TaskProgressListener } from './progressListener';
|
12
12
|
import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
|
13
13
|
import { repositoryManager } from './repositoryManager';
|
14
14
|
import { BlockDefinition, BlockInstance, Plan } from '@kapeta/schemas';
|
15
15
|
import { Actions } from '@kapeta/nodejs-registry-utils';
|
16
16
|
import { definitionsManager } from './definitionsManager';
|
17
|
-
import { taskManager } from './taskManager';
|
18
|
-
import { SourceOfChange } from './types';
|
17
|
+
import { Task, taskManager } from './taskManager';
|
18
|
+
import { KIND_BLOCK_TYPE_EXECUTABLE, KIND_BLOCK_TYPE_OPERATOR, KIND_BLOCK_TYPE, SourceOfChange } from './types';
|
19
19
|
import { cacheManager } from './cacheManager';
|
20
20
|
import uuid from 'node-uuid';
|
21
21
|
import os from 'node:os';
|
@@ -94,8 +94,9 @@ class AssetManager {
|
|
94
94
|
async getAssets(assetKinds?: string[]): Promise<EnrichedAsset[]> {
|
95
95
|
if (!assetKinds) {
|
96
96
|
const blockTypeProviders = await definitionsManager.getDefinitions([
|
97
|
-
|
98
|
-
|
97
|
+
KIND_BLOCK_TYPE,
|
98
|
+
KIND_BLOCK_TYPE_OPERATOR,
|
99
|
+
KIND_BLOCK_TYPE_EXECUTABLE,
|
99
100
|
]);
|
100
101
|
assetKinds = blockTypeProviders.map((p) => {
|
101
102
|
return `${p.definition.metadata.name}:${p.version}`;
|
@@ -307,11 +308,12 @@ class AssetManager {
|
|
307
308
|
}
|
308
309
|
|
309
310
|
console.log('Installing updates', refs);
|
310
|
-
const updateAll = async () => {
|
311
|
+
const updateAll = async (task: Task) => {
|
312
|
+
const progressListener = new TaskProgressListener(task);
|
311
313
|
try {
|
312
314
|
//We change to a temp dir to avoid issues with the current working directory
|
313
315
|
process.chdir(os.tmpdir());
|
314
|
-
await Actions.install(
|
316
|
+
await Actions.install(progressListener, refs, {});
|
315
317
|
await this.cleanupUnusedProviders();
|
316
318
|
} catch (e) {
|
317
319
|
console.error(`Failed to update assets: ${refs.join(',')}`, e);
|
package/src/containerManager.ts
CHANGED
@@ -14,12 +14,12 @@ import ClusterConfiguration from '@kapeta/local-cluster-config';
|
|
14
14
|
import uuid from 'node-uuid';
|
15
15
|
import md5 from 'md5';
|
16
16
|
import { getBlockInstanceContainerName } from './utils/utils';
|
17
|
-
import {
|
17
|
+
import { InstanceInfo, LogEntry, LogSource } from './types';
|
18
18
|
import { KapetaAPI } from '@kapeta/nodejs-api-client';
|
19
19
|
import { taskManager, Task } from './taskManager';
|
20
20
|
import { EventEmitter } from 'node:events';
|
21
21
|
import StreamValues from 'stream-json/streamers/StreamValues';
|
22
|
-
import {
|
22
|
+
import { LocalInstanceHealth } from '@kapeta/schemas';
|
23
23
|
|
24
24
|
type StringMap = { [key: string]: string };
|
25
25
|
|
@@ -100,7 +100,7 @@ enum DockerPullEventTypes {
|
|
100
100
|
|
101
101
|
type DockerPullEventType = DockerPullEventTypes | string;
|
102
102
|
|
103
|
-
const processJsonStream = <T>(purpose: string, stream:
|
103
|
+
const processJsonStream = <T>(purpose: string, stream: NodeJS.ReadableStream, handler: (d: JSONMessage<T>) => void) =>
|
104
104
|
new Promise<void>((resolve, reject) => {
|
105
105
|
const jsonStream = StreamValues.withParser();
|
106
106
|
jsonStream.on('data', (data: any) => {
|
@@ -296,15 +296,8 @@ class ContainerManager {
|
|
296
296
|
}
|
297
297
|
|
298
298
|
async getContainerByName(containerName: string): Promise<ContainerInfo | undefined> {
|
299
|
-
|
300
|
-
|
301
|
-
return container.Names.indexOf(`/${containerName}`) > -1;
|
302
|
-
});
|
303
|
-
|
304
|
-
if (out) {
|
305
|
-
return this.get(out.Id);
|
306
|
-
}
|
307
|
-
return undefined;
|
299
|
+
// The container can be fetched by name or by id using the same API call
|
300
|
+
return this.get(containerName);
|
308
301
|
}
|
309
302
|
|
310
303
|
async pull(image: string) {
|
@@ -398,6 +391,11 @@ class ContainerManager {
|
|
398
391
|
|
399
392
|
const chunk = chunks[data.id];
|
400
393
|
|
394
|
+
if (data.stream) {
|
395
|
+
// Emit raw output to the task log
|
396
|
+
task.addLog(data.stream);
|
397
|
+
}
|
398
|
+
|
401
399
|
switch (data.status) {
|
402
400
|
case DockerPullEventTypes.PreparingPhase:
|
403
401
|
case DockerPullEventTypes.WaitingPhase:
|
@@ -529,7 +527,7 @@ class ContainerManager {
|
|
529
527
|
return Mounts;
|
530
528
|
}
|
531
529
|
|
532
|
-
toDockerHealth(health:
|
530
|
+
toDockerHealth(health: LocalInstanceHealth) {
|
533
531
|
return {
|
534
532
|
Test: ['CMD-SHELL', health.cmd],
|
535
533
|
Interval: health.interval ? health.interval * NANO_SECOND : 5000 * NANO_SECOND,
|
@@ -679,7 +677,7 @@ class ContainerManager {
|
|
679
677
|
let dockerContainer = null;
|
680
678
|
|
681
679
|
try {
|
682
|
-
dockerContainer =
|
680
|
+
dockerContainer = this.docker().getContainer(name);
|
683
681
|
await dockerContainer.stats();
|
684
682
|
} catch (err) {
|
685
683
|
//Ignore
|
@@ -707,7 +705,7 @@ class ContainerManager {
|
|
707
705
|
];
|
708
706
|
}
|
709
707
|
|
710
|
-
return containerInfo.getLogs();
|
708
|
+
return await containerInfo.getLogs();
|
711
709
|
}
|
712
710
|
|
713
711
|
async stopLogListening(systemId: string, instanceId: string) {
|
@@ -782,6 +780,34 @@ class ContainerManager {
|
|
782
780
|
// Ignore
|
783
781
|
}
|
784
782
|
}
|
783
|
+
|
784
|
+
buildDockerImage(dockerFile: string, imageName: string) {
|
785
|
+
const taskName = `Building docker image: ${imageName}`;
|
786
|
+
const processor = async (task: Task) => {
|
787
|
+
const timeStarted = Date.now();
|
788
|
+
const stream = await this.docker().buildImage(
|
789
|
+
{
|
790
|
+
context: Path.dirname(dockerFile),
|
791
|
+
src: [Path.basename(dockerFile)],
|
792
|
+
},
|
793
|
+
{
|
794
|
+
t: imageName,
|
795
|
+
dockerfile: Path.basename(dockerFile),
|
796
|
+
}
|
797
|
+
);
|
798
|
+
|
799
|
+
await processJsonStream<string>(`image:build:${imageName}`, stream, (data) => {
|
800
|
+
if (data.stream) {
|
801
|
+
// Emit raw output to the task log
|
802
|
+
task.addLog(data.stream);
|
803
|
+
}
|
804
|
+
});
|
805
|
+
};
|
806
|
+
|
807
|
+
return taskManager.add(`docker:image:build:${imageName}`, processor, {
|
808
|
+
name: taskName,
|
809
|
+
});
|
810
|
+
}
|
785
811
|
}
|
786
812
|
|
787
813
|
function readLogBuffer(logBuffer: Buffer) {
|
@@ -1038,17 +1064,78 @@ export class ContainerInfo {
|
|
1038
1064
|
});
|
1039
1065
|
|
1040
1066
|
const out = readLogBuffer(logs);
|
1067
|
+
if (out.length > 0) {
|
1068
|
+
return out;
|
1069
|
+
}
|
1041
1070
|
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1071
|
+
const status = await this.status();
|
1072
|
+
const healthLogs: LogEntry[] = status?.Health?.Log
|
1073
|
+
? status?.Health?.Log.map((log) => {
|
1074
|
+
return {
|
1075
|
+
source: 'stdout',
|
1076
|
+
level: log.ExitCode === 0 ? 'INFO' : 'ERROR',
|
1077
|
+
time: Date.now(),
|
1078
|
+
message: 'Health check: ' + log.Output,
|
1079
|
+
};
|
1080
|
+
})
|
1081
|
+
: [];
|
1082
|
+
|
1083
|
+
if (status?.Running) {
|
1084
|
+
return [
|
1085
|
+
{
|
1086
|
+
source: 'stdout',
|
1087
|
+
level: 'INFO',
|
1088
|
+
time: Date.now(),
|
1089
|
+
message: 'Container is starting...',
|
1090
|
+
},
|
1091
|
+
...healthLogs,
|
1092
|
+
];
|
1049
1093
|
}
|
1050
1094
|
|
1051
|
-
|
1095
|
+
if (status?.Restarting) {
|
1096
|
+
return [
|
1097
|
+
{
|
1098
|
+
source: 'stdout',
|
1099
|
+
level: 'INFO',
|
1100
|
+
time: Date.now(),
|
1101
|
+
message: 'Container is restarting...',
|
1102
|
+
},
|
1103
|
+
...healthLogs,
|
1104
|
+
];
|
1105
|
+
}
|
1106
|
+
if (status?.Paused) {
|
1107
|
+
return [
|
1108
|
+
{
|
1109
|
+
source: 'stdout',
|
1110
|
+
level: 'INFO',
|
1111
|
+
time: Date.now(),
|
1112
|
+
message: 'Container is paused...',
|
1113
|
+
},
|
1114
|
+
...healthLogs,
|
1115
|
+
];
|
1116
|
+
}
|
1117
|
+
|
1118
|
+
if (status?.Error) {
|
1119
|
+
return [
|
1120
|
+
{
|
1121
|
+
source: 'stderr',
|
1122
|
+
level: 'ERROR',
|
1123
|
+
time: Date.now(),
|
1124
|
+
message: 'Container failed to start:\n' + status.Error,
|
1125
|
+
},
|
1126
|
+
...healthLogs,
|
1127
|
+
];
|
1128
|
+
}
|
1129
|
+
|
1130
|
+
return [
|
1131
|
+
{
|
1132
|
+
source: 'stdout',
|
1133
|
+
level: 'INFO',
|
1134
|
+
time: Date.now(),
|
1135
|
+
message: 'Container not running',
|
1136
|
+
...healthLogs,
|
1137
|
+
},
|
1138
|
+
];
|
1052
1139
|
}
|
1053
1140
|
}
|
1054
1141
|
|