@kapeta/local-cluster-service 0.10.1 → 0.11.1
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/CHANGELOG.md +14 -0
- package/dist/cjs/src/containerManager.d.ts +6 -4
- package/dist/cjs/src/containerManager.js +100 -45
- package/dist/cjs/src/definitionsManager.d.ts +1 -0
- package/dist/cjs/src/definitionsManager.js +7 -0
- package/dist/cjs/src/instanceManager.d.ts +6 -2
- package/dist/cjs/src/instanceManager.js +240 -233
- package/dist/cjs/src/instances/routes.js +10 -4
- package/dist/cjs/src/operatorManager.js +8 -6
- package/dist/cjs/src/repositoryManager.js +4 -4
- package/dist/cjs/src/types.d.ts +0 -9
- package/dist/cjs/src/utils/BlockInstanceRunner.js +9 -64
- package/dist/cjs/src/utils/utils.d.ts +1 -1
- package/dist/cjs/src/utils/utils.js +3 -2
- package/dist/esm/src/containerManager.d.ts +6 -4
- package/dist/esm/src/containerManager.js +100 -45
- package/dist/esm/src/definitionsManager.d.ts +1 -0
- package/dist/esm/src/definitionsManager.js +7 -0
- package/dist/esm/src/instanceManager.d.ts +6 -2
- package/dist/esm/src/instanceManager.js +240 -233
- package/dist/esm/src/instances/routes.js +10 -4
- package/dist/esm/src/operatorManager.js +8 -6
- package/dist/esm/src/repositoryManager.js +4 -4
- package/dist/esm/src/types.d.ts +0 -9
- package/dist/esm/src/utils/BlockInstanceRunner.js +9 -64
- package/dist/esm/src/utils/utils.d.ts +1 -1
- package/dist/esm/src/utils/utils.js +3 -2
- package/package.json +3 -1
- package/src/containerManager.ts +126 -49
- package/src/definitionsManager.ts +8 -0
- package/src/instanceManager.ts +270 -255
- package/src/instances/routes.ts +9 -4
- package/src/operatorManager.ts +9 -8
- package/src/repositoryManager.ts +5 -5
- package/src/types.ts +0 -7
- package/src/utils/BlockInstanceRunner.ts +10 -66
- package/src/utils/LogData.ts +1 -0
- package/src/utils/utils.ts +3 -2
@@ -113,9 +113,11 @@ class RepositoryManager {
|
|
113
113
|
this._installQueue.push(async () => {
|
114
114
|
try {
|
115
115
|
const normalizedRefs = refs.map((ref) => (0, nodejs_utils_1.parseKapetaUri)(ref).id);
|
116
|
-
const filteredRefs = normalizedRefs
|
117
|
-
|
116
|
+
const filteredRefs = normalizedRefs
|
117
|
+
.filter((ref) => !INSTALL_ATTEMPTED[ref])
|
118
|
+
.filter((ref) => !definitionsManager_1.definitionsManager.exists(ref));
|
118
119
|
if (filteredRefs.length > 0) {
|
120
|
+
console.log(`Auto-installing dependencies: ${filteredRefs.join(', ')}`);
|
119
121
|
filteredRefs.forEach((ref) => (INSTALL_ATTEMPTED[ref] = true));
|
120
122
|
//Auto-install missing asset
|
121
123
|
try {
|
@@ -206,14 +208,12 @@ class RepositoryManager {
|
|
206
208
|
}
|
207
209
|
this._cache[ref] = true;
|
208
210
|
if (!installedAsset) {
|
209
|
-
console.log(`Auto-installing missing asset: ${ref}`);
|
210
211
|
await this._install([ref]);
|
211
212
|
}
|
212
213
|
else {
|
213
214
|
//Ensure dependencies are installed
|
214
215
|
const refs = assetVersion.dependencies.map((dep) => dep.name);
|
215
216
|
if (refs.length > 0) {
|
216
|
-
console.log(`Auto-installing dependencies: ${refs.join(', ')}`);
|
217
217
|
await this._install(refs);
|
218
218
|
}
|
219
219
|
}
|
package/dist/cjs/src/types.d.ts
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
/// <reference types="node" />
|
2
|
-
import EventEmitter from 'events';
|
3
1
|
import express from 'express';
|
4
2
|
import { Resource } from '@kapeta/schemas';
|
5
3
|
import { StringBodyRequest } from './middleware/stringBody';
|
@@ -50,10 +48,7 @@ export declare enum DesiredInstanceStatus {
|
|
50
48
|
export type ProcessInfo = {
|
51
49
|
type: InstanceType;
|
52
50
|
pid?: number | string | null;
|
53
|
-
output: EventEmitter;
|
54
51
|
portType?: string;
|
55
|
-
logs: () => LogEntry[];
|
56
|
-
stop: () => Promise<void> | void;
|
57
52
|
};
|
58
53
|
export type InstanceInfo = {
|
59
54
|
systemId: string;
|
@@ -69,10 +64,6 @@ export type InstanceInfo = {
|
|
69
64
|
health?: string | null;
|
70
65
|
pid?: number | string | null;
|
71
66
|
portType?: string;
|
72
|
-
internal?: {
|
73
|
-
output: EventEmitter;
|
74
|
-
logs: () => LogEntry[];
|
75
|
-
};
|
76
67
|
};
|
77
68
|
interface ResourceRef {
|
78
69
|
blockId: string;
|
@@ -11,7 +11,6 @@ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
|
11
11
|
const serviceManager_1 = require("../serviceManager");
|
12
12
|
const containerManager_1 = require("../containerManager");
|
13
13
|
const LogData_1 = require("./LogData");
|
14
|
-
const events_1 = __importDefault(require("events"));
|
15
14
|
const clusterService_1 = require("../clusterService");
|
16
15
|
const types_1 = require("../types");
|
17
16
|
const definitionsManager_1 = require("../definitionsManager");
|
@@ -133,7 +132,7 @@ class BlockInstanceRunner {
|
|
133
132
|
if (!dockerImage) {
|
134
133
|
throw new Error(`Missing docker image information: ${JSON.stringify(localContainer)}`);
|
135
134
|
}
|
136
|
-
const containerName = (0, utils_1.getBlockInstanceContainerName)(blockInstance.id);
|
135
|
+
const containerName = (0, utils_1.getBlockInstanceContainerName)(this._systemId, blockInstance.id);
|
137
136
|
const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
|
138
137
|
const dockerOpts = localContainer.options ?? {};
|
139
138
|
const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
|
@@ -186,7 +185,7 @@ class BlockInstanceRunner {
|
|
186
185
|
throw new Error(`Missing docker image information: ${JSON.stringify(versionInfo?.artifact?.details)}`);
|
187
186
|
}
|
188
187
|
const { PortBindings, ExposedPorts, addonEnv } = await this.getDockerPortBindings(blockInstance, assetVersion);
|
189
|
-
const containerName = (0, utils_1.getBlockInstanceContainerName)(blockInstance.id);
|
188
|
+
const containerName = (0, utils_1.getBlockInstanceContainerName)(this._systemId, blockInstance.id);
|
190
189
|
// For windows we need to default to root
|
191
190
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : local_cluster_config_1.default.getKapetaBasedir();
|
192
191
|
return this.ensureContainer({
|
@@ -231,7 +230,8 @@ class BlockInstanceRunner {
|
|
231
230
|
throw new Error(`Provider did not have local image: ${providerRef}`);
|
232
231
|
}
|
233
232
|
const dockerImage = spec?.local?.image;
|
234
|
-
|
233
|
+
//We only want 1 operator per operator type - across all local systems
|
234
|
+
const containerName = (0, utils_1.getBlockInstanceContainerName)(this._systemId, blockInstance.id);
|
235
235
|
const logs = new LogData_1.LogData();
|
236
236
|
const bindHost = (0, utils_1.getBindHost)();
|
237
237
|
const ExposedPorts = {};
|
@@ -258,7 +258,7 @@ class BlockInstanceRunner {
|
|
258
258
|
});
|
259
259
|
}
|
260
260
|
if (spec.local?.mounts) {
|
261
|
-
const mounts = containerManager_1.containerManager.createMounts(blockUri.id, spec.local.mounts);
|
261
|
+
const mounts = await containerManager_1.containerManager.createMounts(this._systemId, blockUri.id, spec.local.mounts);
|
262
262
|
Mounts = containerManager_1.containerManager.toDockerMounts(mounts);
|
263
263
|
}
|
264
264
|
if (spec.local?.health) {
|
@@ -323,69 +323,14 @@ class BlockInstanceRunner {
|
|
323
323
|
return { PortBindings, ExposedPorts, addonEnv };
|
324
324
|
}
|
325
325
|
async ensureContainer(opts) {
|
326
|
-
const logs = new LogData_1.LogData();
|
327
326
|
const container = await containerManager_1.containerManager.ensureContainer(opts);
|
328
|
-
|
329
|
-
|
330
|
-
await containerManager_1.containerManager.waitForHealthy(container);
|
331
|
-
}
|
332
|
-
else {
|
333
|
-
await containerManager_1.containerManager.waitForReady(container);
|
334
|
-
}
|
335
|
-
}
|
336
|
-
catch (e) {
|
337
|
-
logs.addLog(e.message, 'ERROR');
|
338
|
-
}
|
339
|
-
return this._handleContainer(container, logs);
|
327
|
+
await containerManager_1.containerManager.waitForReady(container);
|
328
|
+
return this._handleContainer(container);
|
340
329
|
}
|
341
|
-
async _handleContainer(container
|
342
|
-
let localContainer = container;
|
343
|
-
const logStream = (await container.logs({
|
344
|
-
follow: true,
|
345
|
-
stdout: true,
|
346
|
-
stderr: true,
|
347
|
-
tail: LogData_1.LogData.MAX_LINES,
|
348
|
-
}));
|
349
|
-
const outputEvents = new events_1.default();
|
350
|
-
logStream.on('data', (data) => {
|
351
|
-
logs.addLog(data.toString());
|
352
|
-
outputEvents.emit('data', data);
|
353
|
-
});
|
354
|
-
logStream.on('error', (data) => {
|
355
|
-
logs.addLog(data.toString());
|
356
|
-
outputEvents.emit('data', data);
|
357
|
-
});
|
358
|
-
logStream.on('close', async () => {
|
359
|
-
const status = await container.status();
|
360
|
-
const data = status.data;
|
361
|
-
if (deleteOnExit) {
|
362
|
-
try {
|
363
|
-
await containerManager_1.containerManager.remove(container);
|
364
|
-
}
|
365
|
-
catch (e) { }
|
366
|
-
}
|
367
|
-
outputEvents.emit('exit', data?.State?.ExitCode ?? 0);
|
368
|
-
});
|
330
|
+
async _handleContainer(container) {
|
369
331
|
return {
|
370
332
|
type: types_1.InstanceType.DOCKER,
|
371
|
-
pid: container.id
|
372
|
-
output: outputEvents,
|
373
|
-
stop: async () => {
|
374
|
-
if (!localContainer) {
|
375
|
-
return;
|
376
|
-
}
|
377
|
-
try {
|
378
|
-
await localContainer.stop();
|
379
|
-
if (deleteOnExit) {
|
380
|
-
await containerManager_1.containerManager.remove(localContainer);
|
381
|
-
}
|
382
|
-
}
|
383
|
-
catch (e) { }
|
384
|
-
localContainer = null;
|
385
|
-
},
|
386
|
-
logs: () => {
|
387
|
-
return logs.getLogs();
|
388
|
-
},
|
333
|
+
pid: container.id
|
389
334
|
};
|
390
335
|
}
|
391
336
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
export declare function getBlockInstanceContainerName(instanceId: string): string;
|
1
|
+
export declare function getBlockInstanceContainerName(systemId: string, instanceId: string): string;
|
2
2
|
export declare function normalizeKapetaUri(uri: string): string;
|
3
3
|
export declare function readYML(path: string): any;
|
4
4
|
export declare function isWindows(): boolean;
|
@@ -7,8 +7,9 @@ exports.getBindHost = exports.isLinux = exports.isMac = exports.isWindows = expo
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
8
8
|
const yaml_1 = __importDefault(require("yaml"));
|
9
9
|
const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
10
|
-
|
11
|
-
|
10
|
+
const md5_1 = __importDefault(require("md5"));
|
11
|
+
function getBlockInstanceContainerName(systemId, instanceId) {
|
12
|
+
return `kapeta-block-instance-${(0, md5_1.default)(systemId + instanceId)}`;
|
12
13
|
}
|
13
14
|
exports.getBlockInstanceContainerName = getBlockInstanceContainerName;
|
14
15
|
function normalizeKapetaUri(uri) {
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { Docker } from 'node-docker-api';
|
2
2
|
import { Container } from 'node-docker-api/lib/container';
|
3
|
+
import { InstanceInfo, LogEntry } from "./types";
|
3
4
|
type StringMap = {
|
4
5
|
[key: string]: string;
|
5
6
|
};
|
@@ -52,8 +53,8 @@ declare class ContainerManager {
|
|
52
53
|
initialize(): Promise<void>;
|
53
54
|
checkAlive(): Promise<boolean>;
|
54
55
|
isAlive(): boolean;
|
55
|
-
getMountPoint(
|
56
|
-
createMounts(kind: string, mountOpts: StringMap): StringMap
|
56
|
+
getMountPoint(systemId: string, ref: string, mountName: string): string;
|
57
|
+
createMounts(systemId: string, kind: string, mountOpts: StringMap | null | undefined): Promise<StringMap>;
|
57
58
|
ping(): Promise<void>;
|
58
59
|
docker(): Docker;
|
59
60
|
getContainerByName(containerName: string): Promise<ContainerInfo | undefined>;
|
@@ -67,11 +68,10 @@ declare class ContainerManager {
|
|
67
68
|
};
|
68
69
|
private applyHash;
|
69
70
|
ensureContainer(opts: any): Promise<Container>;
|
71
|
+
private createOrUpdateContainer;
|
70
72
|
startContainer(opts: any): Promise<Container>;
|
71
73
|
waitForReady(container: Container, attempt?: number): Promise<void>;
|
72
|
-
waitForHealthy(container: Container, attempt?: number): Promise<void>;
|
73
74
|
_isReady(container: Container): Promise<any>;
|
74
|
-
_isHealthy(container: Container): Promise<boolean>;
|
75
75
|
remove(container: Container, opts?: {
|
76
76
|
force?: boolean;
|
77
77
|
}): Promise<void>;
|
@@ -81,6 +81,7 @@ declare class ContainerManager {
|
|
81
81
|
* @return {Promise<ContainerInfo>}
|
82
82
|
*/
|
83
83
|
get(name: string): Promise<ContainerInfo | null>;
|
84
|
+
getLogs(instance: InstanceInfo): Promise<LogEntry[]>;
|
84
85
|
}
|
85
86
|
export declare class ContainerInfo {
|
86
87
|
private readonly _container;
|
@@ -105,6 +106,7 @@ export declare class ContainerInfo {
|
|
105
106
|
inspect(): Promise<any>;
|
106
107
|
status(): Promise<DockerState>;
|
107
108
|
getPorts(): Promise<PortMap | false>;
|
109
|
+
getLogs(): Promise<LogEntry[]>;
|
108
110
|
}
|
109
111
|
export declare function getExtraHosts(dockerVersion: string): string[] | undefined;
|
110
112
|
/**
|
@@ -8,6 +8,7 @@ import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
|
8
8
|
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
9
9
|
import uuid from 'node-uuid';
|
10
10
|
import md5 from 'md5';
|
11
|
+
import { getBlockInstanceContainerName } from "./utils/utils";
|
11
12
|
export const CONTAINER_LABEL_PORT_PREFIX = 'kapeta_port-';
|
12
13
|
const NANO_SECOND = 1000000;
|
13
14
|
const HEALTH_CHECK_INTERVAL = 3000;
|
@@ -15,8 +16,8 @@ const HEALTH_CHECK_MAX = 20;
|
|
15
16
|
const IMAGE_PULL_CACHE_TTL = 30 * 60 * 1000;
|
16
17
|
const IMAGE_PULL_CACHE = {};
|
17
18
|
export const HEALTH_CHECK_TIMEOUT = HEALTH_CHECK_INTERVAL * HEALTH_CHECK_MAX * 2;
|
18
|
-
const promisifyStream = (stream) => new Promise((resolve, reject) => {
|
19
|
-
stream.on('data',
|
19
|
+
const promisifyStream = (stream, handler) => new Promise((resolve, reject) => {
|
20
|
+
stream.on('data', handler);
|
20
21
|
stream.on('end', resolve);
|
21
22
|
stream.on('error', reject);
|
22
23
|
});
|
@@ -95,17 +96,21 @@ class ContainerManager {
|
|
95
96
|
isAlive() {
|
96
97
|
return this._alive;
|
97
98
|
}
|
98
|
-
getMountPoint(
|
99
|
-
const kindUri = parseKapetaUri(
|
100
|
-
|
99
|
+
getMountPoint(systemId, ref, mountName) {
|
100
|
+
const kindUri = parseKapetaUri(ref);
|
101
|
+
const systemUri = parseKapetaUri(systemId);
|
102
|
+
return Path.join(this._mountDir, systemUri.handle, systemUri.name, systemUri.version, kindUri.handle, kindUri.name, kindUri.version, mountName);
|
101
103
|
}
|
102
|
-
createMounts(kind, mountOpts) {
|
104
|
+
async createMounts(systemId, kind, mountOpts) {
|
103
105
|
const mounts = {};
|
104
|
-
|
105
|
-
const
|
106
|
-
|
107
|
-
|
108
|
-
|
106
|
+
if (mountOpts) {
|
107
|
+
const mountOptList = Object.entries(mountOpts);
|
108
|
+
for (const [mountName, containerPath] of mountOptList) {
|
109
|
+
const hostPath = this.getMountPoint(systemId, kind, mountName);
|
110
|
+
await FSExtra.mkdirp(hostPath);
|
111
|
+
mounts[containerPath] = hostPath;
|
112
|
+
}
|
113
|
+
}
|
109
114
|
return mounts;
|
110
115
|
}
|
111
116
|
async ping() {
|
@@ -156,12 +161,14 @@ class ContainerManager {
|
|
156
161
|
return false;
|
157
162
|
}
|
158
163
|
console.log('Pulling image: %s', image);
|
159
|
-
await this.docker()
|
164
|
+
const stream = await this.docker()
|
160
165
|
.image.create({}, {
|
161
166
|
fromImage: imageName,
|
162
167
|
tag: tag,
|
163
|
-
})
|
164
|
-
|
168
|
+
});
|
169
|
+
await promisifyStream(stream, (chunk) => {
|
170
|
+
console.log('Data from docker: "%s"', chunk.toString());
|
171
|
+
});
|
165
172
|
IMAGE_PULL_CACHE[image] = Date.now();
|
166
173
|
console.log('Image pulled: %s', image);
|
167
174
|
return true;
|
@@ -198,6 +205,11 @@ class ContainerManager {
|
|
198
205
|
dockerOpts.Labels.HASH = hash;
|
199
206
|
}
|
200
207
|
async ensureContainer(opts) {
|
208
|
+
const container = await this.createOrUpdateContainer(opts);
|
209
|
+
await this.waitForReady(container);
|
210
|
+
return container;
|
211
|
+
}
|
212
|
+
async createOrUpdateContainer(opts) {
|
201
213
|
let imagePulled = false;
|
202
214
|
try {
|
203
215
|
imagePulled = await this.pull(opts.Image);
|
@@ -277,28 +289,6 @@ class ContainerManager {
|
|
277
289
|
}, HEALTH_CHECK_INTERVAL);
|
278
290
|
});
|
279
291
|
}
|
280
|
-
async waitForHealthy(container, attempt) {
|
281
|
-
if (!attempt) {
|
282
|
-
attempt = 0;
|
283
|
-
}
|
284
|
-
if (attempt >= HEALTH_CHECK_MAX) {
|
285
|
-
throw new Error('Container did not become healthy within the timeout');
|
286
|
-
}
|
287
|
-
if (await this._isHealthy(container)) {
|
288
|
-
return;
|
289
|
-
}
|
290
|
-
return new Promise((resolve, reject) => {
|
291
|
-
setTimeout(async () => {
|
292
|
-
try {
|
293
|
-
await this.waitForHealthy(container, (attempt ?? 0) + 1);
|
294
|
-
resolve();
|
295
|
-
}
|
296
|
-
catch (err) {
|
297
|
-
reject(err);
|
298
|
-
}
|
299
|
-
}, HEALTH_CHECK_INTERVAL);
|
300
|
-
});
|
301
|
-
}
|
302
292
|
async _isReady(container) {
|
303
293
|
let info;
|
304
294
|
try {
|
@@ -312,16 +302,12 @@ class ContainerManager {
|
|
312
302
|
if (state?.Status === 'exited' || state?.Status === 'removing' || state?.Status === 'dead') {
|
313
303
|
throw new Error('Container exited unexpectedly');
|
314
304
|
}
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
try {
|
319
|
-
const info = await container.status();
|
320
|
-
const infoData = info?.data;
|
321
|
-
return infoData?.State?.Health?.Status === 'healthy';
|
305
|
+
if (infoData?.State?.Health) {
|
306
|
+
// If container has health info - wait for it to become healthy
|
307
|
+
return infoData.State.Health.Status === 'healthy';
|
322
308
|
}
|
323
|
-
|
324
|
-
return false;
|
309
|
+
else {
|
310
|
+
return infoData?.State?.Running ?? false;
|
325
311
|
}
|
326
312
|
}
|
327
313
|
async remove(container, opts) {
|
@@ -351,6 +337,19 @@ class ContainerManager {
|
|
351
337
|
}
|
352
338
|
return new ContainerInfo(dockerContainer);
|
353
339
|
}
|
340
|
+
async getLogs(instance) {
|
341
|
+
const containerName = getBlockInstanceContainerName(instance.systemId, instance.instanceId);
|
342
|
+
const containerInfo = await this.getContainerByName(containerName);
|
343
|
+
if (!containerInfo) {
|
344
|
+
return [{
|
345
|
+
source: "stdout",
|
346
|
+
level: "ERROR",
|
347
|
+
time: Date.now(),
|
348
|
+
message: "Container not found"
|
349
|
+
}];
|
350
|
+
}
|
351
|
+
return containerInfo.getLogs();
|
352
|
+
}
|
354
353
|
}
|
355
354
|
export class ContainerInfo {
|
356
355
|
_container;
|
@@ -434,6 +433,62 @@ export class ContainerInfo {
|
|
434
433
|
});
|
435
434
|
return ports;
|
436
435
|
}
|
436
|
+
async getLogs() {
|
437
|
+
const logStream = await this.native.logs({
|
438
|
+
stdout: true,
|
439
|
+
stderr: true,
|
440
|
+
follow: false,
|
441
|
+
tail: 100,
|
442
|
+
timestamps: true,
|
443
|
+
});
|
444
|
+
const out = [];
|
445
|
+
await promisifyStream(logStream, (data) => {
|
446
|
+
const buf = data;
|
447
|
+
let offset = 0;
|
448
|
+
while (offset < buf.length) {
|
449
|
+
try {
|
450
|
+
// Read the docker log format - explained here:
|
451
|
+
// https://docs.docker.com/engine/api/v1.41/#operation/ContainerAttach
|
452
|
+
// or here : https://ahmet.im/blog/docker-logs-api-binary-format-explained/
|
453
|
+
// First byte is stream type
|
454
|
+
const streamTypeInt = buf.readInt8(offset);
|
455
|
+
const streamType = streamTypeInt === 1 ? 'stdout' : 'stderr';
|
456
|
+
// Bytes 4-8 is frame size
|
457
|
+
const messageLength = buf.readInt32BE(offset + 4);
|
458
|
+
// After that is the message - with the message length
|
459
|
+
const dataWithoutStreamType = buf.subarray(offset + 8, offset + 8 + messageLength);
|
460
|
+
const raw = dataWithoutStreamType.toString();
|
461
|
+
// Split the message into date and message
|
462
|
+
const firstSpaceIx = raw.indexOf(' ');
|
463
|
+
const dateString = raw.substring(0, firstSpaceIx);
|
464
|
+
const line = raw.substring(firstSpaceIx + 1);
|
465
|
+
offset = offset + messageLength + 8;
|
466
|
+
if (!dateString) {
|
467
|
+
continue;
|
468
|
+
}
|
469
|
+
out.push({
|
470
|
+
time: new Date(dateString).getTime(),
|
471
|
+
message: line,
|
472
|
+
level: 'INFO',
|
473
|
+
source: streamType,
|
474
|
+
});
|
475
|
+
}
|
476
|
+
catch (err) {
|
477
|
+
console.error('Error parsing log entry', err);
|
478
|
+
offset = buf.length;
|
479
|
+
}
|
480
|
+
}
|
481
|
+
});
|
482
|
+
if (out.length === 0) {
|
483
|
+
out.push({
|
484
|
+
time: Date.now(),
|
485
|
+
message: 'No logs found for container',
|
486
|
+
level: 'INFO',
|
487
|
+
source: 'stdout',
|
488
|
+
});
|
489
|
+
}
|
490
|
+
return out;
|
491
|
+
}
|
437
492
|
}
|
438
493
|
export function getExtraHosts(dockerVersion) {
|
439
494
|
if (process.platform !== 'darwin' && process.platform !== 'win32') {
|
@@ -5,6 +5,7 @@ declare class DefinitionsManager {
|
|
5
5
|
clearCache(): void;
|
6
6
|
private doCached;
|
7
7
|
getDefinitions(kindFilter?: string | string[]): DefinitionInfo[];
|
8
|
+
exists(ref: string): boolean;
|
8
9
|
getProviderDefinitions(): DefinitionInfo[];
|
9
10
|
}
|
10
11
|
export declare const definitionsManager: DefinitionsManager;
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
2
|
+
import { parseKapetaUri } from "@kapeta/nodejs-utils";
|
2
3
|
const CACHE_TTL = 60 * 1000; // 1 min
|
3
4
|
class DefinitionsManager {
|
4
5
|
cache = {};
|
@@ -31,6 +32,12 @@ class DefinitionsManager {
|
|
31
32
|
const key = this.getKey(kindFilter);
|
32
33
|
return this.doCached(key, () => ClusterConfiguration.getDefinitions(kindFilter));
|
33
34
|
}
|
35
|
+
exists(ref) {
|
36
|
+
const uri = parseKapetaUri(ref);
|
37
|
+
return !!this.getDefinitions().find((d) => {
|
38
|
+
return parseKapetaUri(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
|
39
|
+
});
|
40
|
+
}
|
34
41
|
getProviderDefinitions() {
|
35
42
|
return this.doCached('providers', () => ClusterConfiguration.getProviderDefinitions());
|
36
43
|
}
|
@@ -1,12 +1,15 @@
|
|
1
|
-
import { InstanceInfo } from './types';
|
1
|
+
import { InstanceInfo, LogEntry } from './types';
|
2
2
|
export declare class InstanceManager {
|
3
3
|
private _interval;
|
4
4
|
private readonly _instances;
|
5
|
+
private readonly instanceLocks;
|
5
6
|
constructor();
|
6
7
|
private checkInstancesLater;
|
7
8
|
getInstances(): InstanceInfo[];
|
8
9
|
getInstancesForPlan(systemId: string): InstanceInfo[];
|
9
10
|
getInstance(systemId: string, instanceId: string): InstanceInfo | undefined;
|
11
|
+
private exclusive;
|
12
|
+
getLogs(systemId: string, instanceId: string): Promise<LogEntry[]>;
|
10
13
|
saveInternalInstance(instance: InstanceInfo): Promise<InstanceInfo>;
|
11
14
|
/**
|
12
15
|
* Method is called when instance is started from the Kapeta SDKs (e.g. NodeJS SDK)
|
@@ -14,9 +17,10 @@ export declare class InstanceManager {
|
|
14
17
|
*/
|
15
18
|
registerInstanceFromSDK(systemId: string, instanceId: string, info: Omit<InstanceInfo, 'systemId' | 'instanceId'>): Promise<InstanceInfo | undefined>;
|
16
19
|
private getHealthUrl;
|
17
|
-
markAsStopped(systemId: string, instanceId: string): void
|
20
|
+
markAsStopped(systemId: string, instanceId: string): Promise<void>;
|
18
21
|
startAllForPlan(systemId: string): Promise<InstanceInfo[]>;
|
19
22
|
stop(systemId: string, instanceId: string): Promise<void>;
|
23
|
+
private stopInner;
|
20
24
|
stopAllForPlan(systemId: string): Promise<void>;
|
21
25
|
start(systemId: string, instanceId: string): Promise<InstanceInfo>;
|
22
26
|
restart(systemId: string, instanceId: string): Promise<InstanceInfo>;
|