@kapeta/local-cluster-service 0.9.0 → 0.10.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/CHANGELOG.md +14 -0
- package/dist/cjs/src/containerManager.d.ts +7 -8
- package/dist/cjs/src/containerManager.js +78 -65
- package/dist/cjs/src/instanceManager.js +4 -2
- package/dist/cjs/src/operatorManager.js +40 -25
- package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +1 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.js +79 -170
- package/dist/esm/src/containerManager.d.ts +7 -8
- package/dist/esm/src/containerManager.js +77 -64
- package/dist/esm/src/instanceManager.js +4 -2
- package/dist/esm/src/operatorManager.js +42 -27
- package/dist/esm/src/utils/BlockInstanceRunner.d.ts +1 -0
- package/dist/esm/src/utils/BlockInstanceRunner.js +79 -170
- package/package.json +1 -1
- package/src/containerManager.ts +76 -71
- package/src/instanceManager.ts +8 -2
- package/src/operatorManager.ts +52 -26
- package/src/utils/BlockInstanceRunner.ts +88 -177
@@ -128,26 +128,6 @@ export class BlockInstanceRunner {
|
|
128
128
|
throw new Error(`Missing docker image information: ${JSON.stringify(localContainer)}`);
|
129
129
|
}
|
130
130
|
const containerName = getBlockInstanceContainerName(blockInstance.id);
|
131
|
-
const logs = new LogData();
|
132
|
-
logs.addLog(`Starting block ${blockInstance.ref}`);
|
133
|
-
const containerInfo = await containerManager.getContainerByName(containerName);
|
134
|
-
let container = containerInfo?.native;
|
135
|
-
console.log('Starting dev container', containerName);
|
136
|
-
if (container) {
|
137
|
-
console.log(`Dev container already exists. Deleting...`);
|
138
|
-
try {
|
139
|
-
await container.delete({
|
140
|
-
force: true,
|
141
|
-
});
|
142
|
-
}
|
143
|
-
catch (e) {
|
144
|
-
throw new Error('Failed to delete existing container: ' + e.message);
|
145
|
-
}
|
146
|
-
container = undefined;
|
147
|
-
}
|
148
|
-
logs.addLog(`Creating new container for block: ${containerName}`);
|
149
|
-
console.log('Creating new dev container', containerName, dockerImage);
|
150
|
-
await containerManager.pull(dockerImage);
|
151
131
|
const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
|
152
132
|
const dockerOpts = localContainer.options ?? {};
|
153
133
|
const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
|
@@ -176,8 +156,7 @@ export class BlockInstanceRunner {
|
|
176
156
|
if (localContainer.healthcheck) {
|
177
157
|
HealthCheck = containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
|
178
158
|
}
|
179
|
-
|
180
|
-
container = await containerManager.startContainer({
|
159
|
+
return this.ensureContainer({
|
181
160
|
Image: dockerImage,
|
182
161
|
name: containerName,
|
183
162
|
WorkingDir: workingDir,
|
@@ -204,8 +183,12 @@ export class BlockInstanceRunner {
|
|
204
183
|
},
|
205
184
|
...dockerOpts,
|
206
185
|
});
|
186
|
+
}
|
187
|
+
async ensureContainer(opts) {
|
188
|
+
const logs = new LogData();
|
189
|
+
const container = await containerManager.ensureContainer(opts);
|
207
190
|
try {
|
208
|
-
if (HealthCheck) {
|
191
|
+
if (opts.HealthCheck) {
|
209
192
|
await containerManager.waitForHealthy(container);
|
210
193
|
}
|
211
194
|
else {
|
@@ -239,7 +222,7 @@ export class BlockInstanceRunner {
|
|
239
222
|
const data = status.data;
|
240
223
|
if (deleteOnExit) {
|
241
224
|
try {
|
242
|
-
await
|
225
|
+
await containerManager.remove(container);
|
243
226
|
}
|
244
227
|
catch (e) { }
|
245
228
|
}
|
@@ -256,7 +239,7 @@ export class BlockInstanceRunner {
|
|
256
239
|
try {
|
257
240
|
await localContainer.stop();
|
258
241
|
if (deleteOnExit) {
|
259
|
-
await
|
242
|
+
await containerManager.remove(localContainer);
|
260
243
|
}
|
261
244
|
}
|
262
245
|
catch (e) { }
|
@@ -283,45 +266,23 @@ export class BlockInstanceRunner {
|
|
283
266
|
}
|
284
267
|
const containerName = getBlockInstanceContainerName(blockInstance.id);
|
285
268
|
const logs = new LogData();
|
286
|
-
const containerInfo = await containerManager.getContainerByName(containerName);
|
287
|
-
let container = containerInfo?.native;
|
288
269
|
// For windows we need to default to root
|
289
270
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
Labels: {
|
306
|
-
instance: blockInstance.id,
|
307
|
-
},
|
308
|
-
Env: [
|
309
|
-
...DOCKER_ENV_VARS,
|
310
|
-
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
311
|
-
...Object.entries(env).map(([key, value]) => `${key}=${value}`),
|
312
|
-
],
|
313
|
-
HostConfig: {
|
314
|
-
Binds: [`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`],
|
315
|
-
},
|
316
|
-
});
|
317
|
-
try {
|
318
|
-
await containerManager.waitForReady(container);
|
319
|
-
}
|
320
|
-
catch (e) {
|
321
|
-
logs.addLog(e.message, 'ERROR');
|
322
|
-
}
|
323
|
-
}
|
324
|
-
return this._handleContainer(container, logs);
|
271
|
+
return this.ensureContainer({
|
272
|
+
Image: dockerImage,
|
273
|
+
name: containerName,
|
274
|
+
Labels: {
|
275
|
+
instance: blockInstance.id,
|
276
|
+
},
|
277
|
+
Env: [
|
278
|
+
...DOCKER_ENV_VARS,
|
279
|
+
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
280
|
+
...Object.entries(env).map(([key, value]) => `${key}=${value}`),
|
281
|
+
],
|
282
|
+
HostConfig: {
|
283
|
+
Binds: [`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`],
|
284
|
+
},
|
285
|
+
});
|
325
286
|
}
|
326
287
|
/**
|
327
288
|
*
|
@@ -344,120 +305,68 @@ export class BlockInstanceRunner {
|
|
344
305
|
throw new Error(`Provider did not have local image: ${providerRef}`);
|
345
306
|
}
|
346
307
|
const dockerImage = spec?.local?.image;
|
347
|
-
try {
|
348
|
-
await containerManager.pull(dockerImage);
|
349
|
-
}
|
350
|
-
catch (e) {
|
351
|
-
console.warn('Failed to pull image. Continuing...', e);
|
352
|
-
}
|
353
308
|
const containerName = getBlockInstanceContainerName(blockInstance.id);
|
354
309
|
const logs = new LogData();
|
355
|
-
const containerInfo = await containerManager.getContainerByName(containerName);
|
356
|
-
let container = containerInfo?.native;
|
357
|
-
if (container) {
|
358
|
-
const containerData = container.data;
|
359
|
-
if (containerData.State === 'running') {
|
360
|
-
logs.addLog(`Found existing running container for block: ${containerName}`);
|
361
|
-
}
|
362
|
-
else {
|
363
|
-
if (containerData.State?.ExitCode > 0) {
|
364
|
-
logs.addLog(`Container exited with code: ${containerData.State.ExitCode}. Deleting...`);
|
365
|
-
try {
|
366
|
-
await container.delete();
|
367
|
-
}
|
368
|
-
catch (e) { }
|
369
|
-
container = undefined;
|
370
|
-
}
|
371
|
-
else {
|
372
|
-
logs.addLog(`Found existing container for block: ${containerName}. Starting now`);
|
373
|
-
try {
|
374
|
-
await container.start();
|
375
|
-
}
|
376
|
-
catch (e) {
|
377
|
-
console.warn('Failed to start container. Deleting...', e);
|
378
|
-
try {
|
379
|
-
await container.delete();
|
380
|
-
}
|
381
|
-
catch (e) { }
|
382
|
-
container = undefined;
|
383
|
-
}
|
384
|
-
}
|
385
|
-
}
|
386
|
-
}
|
387
310
|
const bindHost = getBindHost();
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
const
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
HostPort: `${publicPort}`,
|
403
|
-
},
|
404
|
-
];
|
405
|
-
});
|
406
|
-
await Promise.all(promises);
|
407
|
-
if (spec.local?.env) {
|
408
|
-
Object.entries(spec.local.env).forEach(([key, value]) => {
|
409
|
-
addonEnv[key] = value;
|
410
|
-
});
|
411
|
-
}
|
412
|
-
if (spec.local?.mounts) {
|
413
|
-
const mounts = containerManager.createMounts(blockUri.id, spec.local.mounts);
|
414
|
-
Mounts = containerManager.toDockerMounts(mounts);
|
415
|
-
}
|
416
|
-
if (spec.local?.health) {
|
417
|
-
HealthCheck = containerManager.toDockerHealth(spec.local?.health);
|
418
|
-
}
|
419
|
-
// For windows we need to default to root
|
420
|
-
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
421
|
-
logs.addLog(`Creating new container for block: ${containerName}`);
|
422
|
-
container = await containerManager.startContainer({
|
423
|
-
Image: dockerImage,
|
424
|
-
name: containerName,
|
425
|
-
ExposedPorts,
|
426
|
-
HealthCheck,
|
427
|
-
HostConfig: {
|
428
|
-
Binds: [
|
429
|
-
`${toLocalBindVolume(kapetaYmlPath)}:/kapeta.yml:ro`,
|
430
|
-
`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`,
|
431
|
-
],
|
432
|
-
PortBindings,
|
433
|
-
Mounts,
|
434
|
-
},
|
435
|
-
Labels: {
|
436
|
-
instance: blockInstance.id,
|
311
|
+
const ExposedPorts = {};
|
312
|
+
const addonEnv = {};
|
313
|
+
const PortBindings = {};
|
314
|
+
let HealthCheck = undefined;
|
315
|
+
let Mounts = [];
|
316
|
+
const promises = Object.entries(spec.local.ports).map(async ([portType, value]) => {
|
317
|
+
const dockerPort = `${value.port}/${value.type}`;
|
318
|
+
ExposedPorts[dockerPort] = {};
|
319
|
+
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = value.port;
|
320
|
+
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
321
|
+
PortBindings[dockerPort] = [
|
322
|
+
{
|
323
|
+
HostIp: bindHost,
|
324
|
+
HostPort: `${publicPort}`,
|
437
325
|
},
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
...addonEnv,
|
445
|
-
}).map(([key, value]) => `${key}=${value}`),
|
446
|
-
],
|
326
|
+
];
|
327
|
+
});
|
328
|
+
await Promise.all(promises);
|
329
|
+
if (spec.local?.env) {
|
330
|
+
Object.entries(spec.local.env).forEach(([key, value]) => {
|
331
|
+
addonEnv[key] = value;
|
447
332
|
});
|
448
|
-
try {
|
449
|
-
if (HealthCheck) {
|
450
|
-
await containerManager.waitForHealthy(container);
|
451
|
-
}
|
452
|
-
else {
|
453
|
-
await containerManager.waitForReady(container);
|
454
|
-
}
|
455
|
-
}
|
456
|
-
catch (e) {
|
457
|
-
logs.addLog(e.message, 'ERROR');
|
458
|
-
}
|
459
333
|
}
|
460
|
-
|
334
|
+
if (spec.local?.mounts) {
|
335
|
+
const mounts = containerManager.createMounts(blockUri.id, spec.local.mounts);
|
336
|
+
Mounts = containerManager.toDockerMounts(mounts);
|
337
|
+
}
|
338
|
+
if (spec.local?.health) {
|
339
|
+
HealthCheck = containerManager.toDockerHealth(spec.local?.health);
|
340
|
+
}
|
341
|
+
// For windows we need to default to root
|
342
|
+
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
343
|
+
logs.addLog(`Creating new container for block: ${containerName}`);
|
344
|
+
const out = await this.ensureContainer({
|
345
|
+
Image: dockerImage,
|
346
|
+
name: containerName,
|
347
|
+
ExposedPorts,
|
348
|
+
HealthCheck,
|
349
|
+
HostConfig: {
|
350
|
+
Binds: [
|
351
|
+
`${toLocalBindVolume(kapetaYmlPath)}:/kapeta.yml:ro`,
|
352
|
+
`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`,
|
353
|
+
],
|
354
|
+
PortBindings,
|
355
|
+
Mounts,
|
356
|
+
},
|
357
|
+
Labels: {
|
358
|
+
instance: blockInstance.id,
|
359
|
+
},
|
360
|
+
Env: [
|
361
|
+
`KAPETA_INSTANCE_NAME=${blockInstance.ref}`,
|
362
|
+
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
363
|
+
...DOCKER_ENV_VARS,
|
364
|
+
...Object.entries({
|
365
|
+
...env,
|
366
|
+
...addonEnv,
|
367
|
+
}).map(([key, value]) => `${key}=${value}`),
|
368
|
+
],
|
369
|
+
});
|
461
370
|
const portTypes = spec.local.ports ? Object.keys(spec.local.ports) : [];
|
462
371
|
if (portTypes.length > 0) {
|
463
372
|
out.portType = portTypes[0];
|
package/package.json
CHANGED
package/src/containerManager.ts
CHANGED
@@ -7,7 +7,8 @@ import { Docker } from 'node-docker-api';
|
|
7
7
|
import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
8
8
|
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
9
9
|
import { Container } from 'node-docker-api/lib/container';
|
10
|
-
import
|
10
|
+
import uuid from 'node-uuid';
|
11
|
+
import md5 from 'md5';
|
11
12
|
|
12
13
|
type StringMap = { [key: string]: string };
|
13
14
|
|
@@ -53,7 +54,7 @@ interface Health {
|
|
53
54
|
retries?: number;
|
54
55
|
}
|
55
56
|
|
56
|
-
const
|
57
|
+
export const CONTAINER_LABEL_PORT_PREFIX = 'kapeta_port-';
|
57
58
|
const NANO_SECOND = 1000000;
|
58
59
|
const HEALTH_CHECK_INTERVAL = 3000;
|
59
60
|
const HEALTH_CHECK_MAX = 20;
|
@@ -205,24 +206,21 @@ class ContainerManager {
|
|
205
206
|
tag = 'latest';
|
206
207
|
}
|
207
208
|
|
208
|
-
if (
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
return;
|
213
|
-
}
|
209
|
+
if (IMAGE_PULL_CACHE[image]) {
|
210
|
+
const timeSince = Date.now() - IMAGE_PULL_CACHE[image];
|
211
|
+
if (timeSince < cacheForMS) {
|
212
|
+
return false;
|
214
213
|
}
|
214
|
+
}
|
215
215
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
216
|
+
const imageTagList = (await this.docker().image.list())
|
217
|
+
.map((image) => image.data as any)
|
218
|
+
.filter((imageData) => !!imageData.RepoTags)
|
219
|
+
.map((imageData) => imageData.RepoTags as string[]);
|
220
220
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
}
|
225
|
-
console.log('Image not found: %s', image);
|
221
|
+
if (imageTagList.some((imageTags) => imageTags.indexOf(image) > -1)) {
|
222
|
+
console.log('Image found: %s', image);
|
223
|
+
return false;
|
226
224
|
}
|
227
225
|
|
228
226
|
console.log('Pulling image: %s', image);
|
@@ -239,6 +237,8 @@ class ContainerManager {
|
|
239
237
|
IMAGE_PULL_CACHE[image] = Date.now();
|
240
238
|
|
241
239
|
console.log('Image pulled: %s', image);
|
240
|
+
|
241
|
+
return true;
|
242
242
|
}
|
243
243
|
|
244
244
|
toDockerMounts(mounts: StringMap) {
|
@@ -265,66 +265,63 @@ class ContainerManager {
|
|
265
265
|
};
|
266
266
|
}
|
267
267
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
): Promise<ContainerInfo> {
|
273
|
-
const PortBindings: { [key: string]: any } = {};
|
274
|
-
const Env: string[] = [];
|
275
|
-
const Labels: StringMap = {
|
276
|
-
kapeta: 'true',
|
277
|
-
};
|
278
|
-
|
279
|
-
await this.pull(image);
|
280
|
-
|
281
|
-
const bindHost = getBindHost();
|
268
|
+
private applyHash(dockerOpts: any) {
|
269
|
+
if (dockerOpts?.Labels?.HASH) {
|
270
|
+
delete dockerOpts.Labels.HASH;
|
271
|
+
}
|
282
272
|
|
283
|
-
const
|
273
|
+
const hash = md5(JSON.stringify(dockerOpts));
|
284
274
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
HostIp: bindHost,
|
291
|
-
},
|
292
|
-
];
|
275
|
+
if (!dockerOpts.Labels) {
|
276
|
+
dockerOpts.Labels = {};
|
277
|
+
}
|
278
|
+
dockerOpts.Labels.HASH = hash;
|
279
|
+
}
|
293
280
|
|
294
|
-
|
295
|
-
|
281
|
+
async ensureContainer(opts: any) {
|
282
|
+
let imagePulled = false;
|
283
|
+
try {
|
284
|
+
imagePulled = await this.pull(opts.Image);
|
285
|
+
} catch (e) {
|
286
|
+
console.warn('Failed to pull image. Continuing...', e);
|
287
|
+
}
|
296
288
|
|
297
|
-
|
289
|
+
this.applyHash(opts);
|
290
|
+
if (!opts.name) {
|
291
|
+
console.log('Starting unnamed container: %s', opts.Image);
|
292
|
+
return this.startContainer(opts);
|
293
|
+
}
|
294
|
+
const containerInfo = await this.getContainerByName(opts.name);
|
295
|
+
if (imagePulled) {
|
296
|
+
console.log('New version of image was pulled: %s', opts.Image);
|
297
|
+
} else {
|
298
|
+
// If image was pulled always recreate
|
299
|
+
if (!containerInfo) {
|
300
|
+
console.log('Starting new container: %s', opts.name);
|
301
|
+
return this.startContainer(opts);
|
302
|
+
}
|
298
303
|
|
299
|
-
|
300
|
-
Env.push(name + '=' + value);
|
301
|
-
});
|
304
|
+
const containerData = containerInfo.native.data as any;
|
302
305
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
Cmd: opts.cmd,
|
314
|
-
ExposedPorts,
|
315
|
-
Env,
|
316
|
-
HealthCheck,
|
317
|
-
HostConfig: {
|
318
|
-
PortBindings,
|
319
|
-
Mounts,
|
320
|
-
},
|
321
|
-
});
|
306
|
+
if (containerData?.Labels?.HASH === opts.Labels.HASH) {
|
307
|
+
if (!(await containerInfo.isRunning())) {
|
308
|
+
console.log('Starting previously created container: %s', opts.name);
|
309
|
+
await containerInfo.start();
|
310
|
+
} else {
|
311
|
+
console.log('Previously created container already running: %s', opts.name);
|
312
|
+
}
|
313
|
+
return containerInfo.native;
|
314
|
+
}
|
315
|
+
}
|
322
316
|
|
323
|
-
if (
|
324
|
-
|
317
|
+
if (containerInfo) {
|
318
|
+
// Remove the container and start a new one
|
319
|
+
console.log('Replacing previously created container: %s', opts.name);
|
320
|
+
await containerInfo.remove({ force: true });
|
325
321
|
}
|
326
322
|
|
327
|
-
|
323
|
+
console.log('Starting new container: %s', opts.name);
|
324
|
+
return this.startContainer(opts);
|
328
325
|
}
|
329
326
|
|
330
327
|
async startContainer(opts: any) {
|
@@ -422,6 +419,14 @@ class ContainerManager {
|
|
422
419
|
}
|
423
420
|
}
|
424
421
|
|
422
|
+
async remove(container: Container, opts?: { force?: boolean }) {
|
423
|
+
const newName = 'deleting-' + uuid.v4();
|
424
|
+
const containerData = container.data as any;
|
425
|
+
// Rename the container first to avoid name conflicts if people start the same container
|
426
|
+
await container.rename({ name: newName });
|
427
|
+
await container.delete({ force: !!opts?.force });
|
428
|
+
}
|
429
|
+
|
425
430
|
/**
|
426
431
|
*
|
427
432
|
* @param name
|
@@ -488,7 +493,7 @@ export class ContainerInfo {
|
|
488
493
|
}
|
489
494
|
|
490
495
|
async remove(opts?: { force?: boolean }) {
|
491
|
-
await this._container
|
496
|
+
await containerManager.remove(this._container, opts);
|
492
497
|
}
|
493
498
|
|
494
499
|
async getPort(type: string) {
|
@@ -528,11 +533,11 @@ export class ContainerInfo {
|
|
528
533
|
const ports: PortMap = {};
|
529
534
|
|
530
535
|
_.forEach(inspectResult.Config.Labels, (portType, name) => {
|
531
|
-
if (!name.startsWith(
|
536
|
+
if (!name.startsWith(CONTAINER_LABEL_PORT_PREFIX)) {
|
532
537
|
return;
|
533
538
|
}
|
534
539
|
|
535
|
-
const hostPort = name.substr(
|
540
|
+
const hostPort = name.substr(CONTAINER_LABEL_PORT_PREFIX.length);
|
536
541
|
|
537
542
|
portTypes[hostPort] = portType;
|
538
543
|
});
|
package/src/instanceManager.ts
CHANGED
@@ -559,7 +559,10 @@ export class InstanceManager {
|
|
559
559
|
}
|
560
560
|
}
|
561
561
|
|
562
|
-
if (
|
562
|
+
if (
|
563
|
+
instance.desiredStatus === DesiredInstanceStatus.RUN &&
|
564
|
+
[InstanceStatus.STOPPED, InstanceStatus.FAILED, InstanceStatus.STOPPING].includes(newStatus)
|
565
|
+
) {
|
563
566
|
//If the instance is stopped but we want it to run, start it
|
564
567
|
try {
|
565
568
|
await this.start(instance.systemId, instance.instanceId);
|
@@ -569,7 +572,10 @@ export class InstanceManager {
|
|
569
572
|
return;
|
570
573
|
}
|
571
574
|
|
572
|
-
if (
|
575
|
+
if (
|
576
|
+
instance.desiredStatus === DesiredInstanceStatus.STOP &&
|
577
|
+
[InstanceStatus.READY, InstanceStatus.STARTING, InstanceStatus.UNHEALTHY].includes(newStatus)
|
578
|
+
) {
|
573
579
|
//If the instance is running but we want it to stop, stop it
|
574
580
|
try {
|
575
581
|
await this.stop(instance.systemId, instance.instanceId);
|
package/src/operatorManager.ts
CHANGED
@@ -4,12 +4,14 @@ import md5 from 'md5';
|
|
4
4
|
import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
5
5
|
import { serviceManager } from './serviceManager';
|
6
6
|
import { storageService } from './storageService';
|
7
|
-
import { ContainerInfo, containerManager } from './containerManager';
|
7
|
+
import { CONTAINER_LABEL_PORT_PREFIX, ContainerInfo, containerManager } from './containerManager';
|
8
8
|
import FSExtra from 'fs-extra';
|
9
|
-
import { AnyMap, EnvironmentType, OperatorInfo } from './types';
|
9
|
+
import { AnyMap, EnvironmentType, OperatorInfo, StringMap } from './types';
|
10
10
|
import { BlockInstance, Resource } from '@kapeta/schemas';
|
11
11
|
import { definitionsManager } from './definitionsManager';
|
12
|
-
import { normalizeKapetaUri } from './utils/utils';
|
12
|
+
import { getBindHost, normalizeKapetaUri } from './utils/utils';
|
13
|
+
import _ from 'lodash';
|
14
|
+
import { Container } from 'node-docker-api/lib/container';
|
13
15
|
|
14
16
|
const KIND_OPERATOR = 'core/resource-type-operator';
|
15
17
|
|
@@ -191,34 +193,58 @@ class OperatorManager {
|
|
191
193
|
const mounts = containerManager.createMounts(resourceType, operatorData.mounts);
|
192
194
|
|
193
195
|
const containerName = containerBaseName + '-' + md5(nameParts.join('_'));
|
194
|
-
let container = await containerManager.get(containerName);
|
195
196
|
|
196
|
-
const
|
197
|
-
|
198
|
-
await container.start();
|
199
|
-
}
|
197
|
+
const PortBindings: { [key: string]: any } = {};
|
198
|
+
const Env: string[] = [];
|
200
199
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
ports,
|
205
|
-
health: operatorData.health,
|
206
|
-
env: operatorData.env,
|
207
|
-
cmd: operatorData.cmd,
|
208
|
-
});
|
209
|
-
}
|
200
|
+
const Labels: StringMap = {
|
201
|
+
kapeta: 'true',
|
202
|
+
};
|
210
203
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
}
|
217
|
-
|
218
|
-
|
204
|
+
const bindHost = getBindHost();
|
205
|
+
|
206
|
+
const ExposedPorts: { [key: string]: any } = {};
|
207
|
+
|
208
|
+
_.forEach(ports, (portInfo: any, containerPort) => {
|
209
|
+
ExposedPorts['' + containerPort] = {};
|
210
|
+
PortBindings['' + containerPort] = [
|
211
|
+
{
|
212
|
+
HostPort: '' + portInfo.hostPort,
|
213
|
+
HostIp: bindHost,
|
214
|
+
},
|
215
|
+
];
|
216
|
+
|
217
|
+
Labels[CONTAINER_LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
|
218
|
+
});
|
219
|
+
|
220
|
+
const Mounts = containerManager.toDockerMounts(mounts);
|
221
|
+
|
222
|
+
_.forEach(operatorData.env, (value, name) => {
|
223
|
+
Env.push(name + '=' + value);
|
224
|
+
});
|
225
|
+
|
226
|
+
let HealthCheck = undefined;
|
227
|
+
|
228
|
+
if (operatorData.health) {
|
229
|
+
HealthCheck = containerManager.toDockerHealth(operatorData.health);
|
219
230
|
}
|
220
231
|
|
221
|
-
|
232
|
+
const container = await containerManager.ensureContainer({
|
233
|
+
name: containerName,
|
234
|
+
Image: operatorData.image,
|
235
|
+
Hostname: containerName + '.kapeta',
|
236
|
+
Labels,
|
237
|
+
Cmd: operatorData.cmd,
|
238
|
+
ExposedPorts,
|
239
|
+
Env,
|
240
|
+
HealthCheck,
|
241
|
+
HostConfig: {
|
242
|
+
PortBindings,
|
243
|
+
Mounts,
|
244
|
+
},
|
245
|
+
});
|
246
|
+
|
247
|
+
return new ContainerInfo(container);
|
222
248
|
}
|
223
249
|
}
|
224
250
|
|