@kapeta/local-cluster-service 0.10.0 → 0.10.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 +7 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +3 -2
- package/dist/cjs/src/utils/BlockInstanceRunner.js +99 -90
- package/dist/esm/src/utils/BlockInstanceRunner.d.ts +3 -2
- package/dist/esm/src/utils/BlockInstanceRunner.js +99 -90
- package/package.json +1 -1
- package/src/utils/BlockInstanceRunner.ts +126 -105
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## [0.10.1](https://github.com/kapetacom/local-cluster-service/compare/v0.10.0...v0.10.1) (2023-07-27)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Include port bindings for non-local containers ([#51](https://github.com/kapetacom/local-cluster-service/issues/51)) ([64fd440](https://github.com/kapetacom/local-cluster-service/commit/64fd4409ea9e2dda8e2438d0ec85a8f5a2092b1e))
|
7
|
+
|
1
8
|
# [0.10.0](https://github.com/kapetacom/local-cluster-service/compare/v0.9.1...v0.10.0) (2023-07-26)
|
2
9
|
|
3
10
|
|
@@ -14,8 +14,6 @@ export declare class BlockInstanceRunner {
|
|
14
14
|
* Starts local process
|
15
15
|
*/
|
16
16
|
private _startLocalProcess;
|
17
|
-
private ensureContainer;
|
18
|
-
private _handleContainer;
|
19
17
|
private _startDockerProcess;
|
20
18
|
/**
|
21
19
|
*
|
@@ -27,4 +25,7 @@ export declare class BlockInstanceRunner {
|
|
27
25
|
* @private
|
28
26
|
*/
|
29
27
|
_startOperatorProcess(blockInstance: BlockProcessParams, blockUri: KapetaURI, providerDefinition: DefinitionInfo, env: StringMap): Promise<ProcessInfo>;
|
28
|
+
private getDockerPortBindings;
|
29
|
+
private ensureContainer;
|
30
|
+
private _handleContainer;
|
30
31
|
}
|
@@ -100,7 +100,7 @@ class BlockInstanceRunner {
|
|
100
100
|
processInfo = await this._startLocalProcess(blockInstance, blockUri, env, assetVersion);
|
101
101
|
}
|
102
102
|
else {
|
103
|
-
processInfo = await this._startDockerProcess(blockInstance, blockUri, env);
|
103
|
+
processInfo = await this._startDockerProcess(blockInstance, blockUri, env, assetVersion);
|
104
104
|
}
|
105
105
|
if (portTypes.length > 0) {
|
106
106
|
processInfo.portType = portTypes[0];
|
@@ -138,26 +138,7 @@ class BlockInstanceRunner {
|
|
138
138
|
const dockerOpts = localContainer.options ?? {};
|
139
139
|
const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
|
140
140
|
const workingDir = localContainer.workingDir ? localContainer.workingDir : '/workspace';
|
141
|
-
const
|
142
|
-
const ExposedPorts = {};
|
143
|
-
const addonEnv = {};
|
144
|
-
const PortBindings = {};
|
145
|
-
const portTypes = getProviderPorts(assetVersion);
|
146
|
-
let port = 80;
|
147
|
-
const promises = portTypes.map(async (portType) => {
|
148
|
-
const publicPort = await serviceManager_1.serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
149
|
-
const thisPort = port++; //TODO: Not sure how we should handle multiple ports or non-HTTP ports
|
150
|
-
const dockerPort = `${thisPort}/tcp`;
|
151
|
-
ExposedPorts[dockerPort] = {};
|
152
|
-
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = '' + thisPort;
|
153
|
-
PortBindings[dockerPort] = [
|
154
|
-
{
|
155
|
-
HostIp: bindHost,
|
156
|
-
HostPort: `${publicPort}`,
|
157
|
-
},
|
158
|
-
];
|
159
|
-
});
|
160
|
-
await Promise.all(promises);
|
141
|
+
const { PortBindings, ExposedPorts, addonEnv } = await this.getDockerPortBindings(blockInstance, assetVersion);
|
161
142
|
let HealthCheck = undefined;
|
162
143
|
if (localContainer.healthcheck) {
|
163
144
|
HealthCheck = containerManager_1.containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
|
@@ -190,73 +171,7 @@ class BlockInstanceRunner {
|
|
190
171
|
...dockerOpts,
|
191
172
|
});
|
192
173
|
}
|
193
|
-
async
|
194
|
-
const logs = new LogData_1.LogData();
|
195
|
-
const container = await containerManager_1.containerManager.ensureContainer(opts);
|
196
|
-
try {
|
197
|
-
if (opts.HealthCheck) {
|
198
|
-
await containerManager_1.containerManager.waitForHealthy(container);
|
199
|
-
}
|
200
|
-
else {
|
201
|
-
await containerManager_1.containerManager.waitForReady(container);
|
202
|
-
}
|
203
|
-
}
|
204
|
-
catch (e) {
|
205
|
-
logs.addLog(e.message, 'ERROR');
|
206
|
-
}
|
207
|
-
return this._handleContainer(container, logs);
|
208
|
-
}
|
209
|
-
async _handleContainer(container, logs, deleteOnExit = false) {
|
210
|
-
let localContainer = container;
|
211
|
-
const logStream = (await container.logs({
|
212
|
-
follow: true,
|
213
|
-
stdout: true,
|
214
|
-
stderr: true,
|
215
|
-
tail: LogData_1.LogData.MAX_LINES,
|
216
|
-
}));
|
217
|
-
const outputEvents = new events_1.default();
|
218
|
-
logStream.on('data', (data) => {
|
219
|
-
logs.addLog(data.toString());
|
220
|
-
outputEvents.emit('data', data);
|
221
|
-
});
|
222
|
-
logStream.on('error', (data) => {
|
223
|
-
logs.addLog(data.toString());
|
224
|
-
outputEvents.emit('data', data);
|
225
|
-
});
|
226
|
-
logStream.on('close', async () => {
|
227
|
-
const status = await container.status();
|
228
|
-
const data = status.data;
|
229
|
-
if (deleteOnExit) {
|
230
|
-
try {
|
231
|
-
await containerManager_1.containerManager.remove(container);
|
232
|
-
}
|
233
|
-
catch (e) { }
|
234
|
-
}
|
235
|
-
outputEvents.emit('exit', data?.State?.ExitCode ?? 0);
|
236
|
-
});
|
237
|
-
return {
|
238
|
-
type: types_1.InstanceType.DOCKER,
|
239
|
-
pid: container.id,
|
240
|
-
output: outputEvents,
|
241
|
-
stop: async () => {
|
242
|
-
if (!localContainer) {
|
243
|
-
return;
|
244
|
-
}
|
245
|
-
try {
|
246
|
-
await localContainer.stop();
|
247
|
-
if (deleteOnExit) {
|
248
|
-
await containerManager_1.containerManager.remove(localContainer);
|
249
|
-
}
|
250
|
-
}
|
251
|
-
catch (e) { }
|
252
|
-
localContainer = null;
|
253
|
-
},
|
254
|
-
logs: () => {
|
255
|
-
return logs.getLogs();
|
256
|
-
},
|
257
|
-
};
|
258
|
-
}
|
259
|
-
async _startDockerProcess(blockInstance, blockInfo, env) {
|
174
|
+
async _startDockerProcess(blockInstance, blockInfo, env, assetVersion) {
|
260
175
|
const { versionFile } = local_cluster_config_1.default.getRepositoryAssetInfoPath(blockInfo.handle, blockInfo.name, blockInfo.version);
|
261
176
|
const versionYml = versionFile;
|
262
177
|
if (!node_fs_1.default.existsSync(versionYml)) {
|
@@ -270,23 +185,28 @@ class BlockInstanceRunner {
|
|
270
185
|
if (!dockerImage) {
|
271
186
|
throw new Error(`Missing docker image information: ${JSON.stringify(versionInfo?.artifact?.details)}`);
|
272
187
|
}
|
188
|
+
const { PortBindings, ExposedPorts, addonEnv } = await this.getDockerPortBindings(blockInstance, assetVersion);
|
273
189
|
const containerName = (0, utils_1.getBlockInstanceContainerName)(blockInstance.id);
|
274
|
-
const logs = new LogData_1.LogData();
|
275
190
|
// For windows we need to default to root
|
276
191
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : local_cluster_config_1.default.getKapetaBasedir();
|
277
192
|
return this.ensureContainer({
|
278
193
|
Image: dockerImage,
|
279
194
|
name: containerName,
|
195
|
+
ExposedPorts,
|
280
196
|
Labels: {
|
281
197
|
instance: blockInstance.id,
|
282
198
|
},
|
283
199
|
Env: [
|
284
200
|
...DOCKER_ENV_VARS,
|
285
201
|
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService_1.clusterService.getClusterServicePort()}`,
|
286
|
-
...Object.entries(
|
202
|
+
...Object.entries({
|
203
|
+
...env,
|
204
|
+
...addonEnv
|
205
|
+
}).map(([key, value]) => `${key}=${value}`),
|
287
206
|
],
|
288
207
|
HostConfig: {
|
289
208
|
Binds: [`${(0, containerManager_1.toLocalBindVolume)(local_cluster_config_1.default.getKapetaBasedir())}:${innerHome}`],
|
209
|
+
PortBindings,
|
290
210
|
},
|
291
211
|
});
|
292
212
|
}
|
@@ -379,5 +299,94 @@ class BlockInstanceRunner {
|
|
379
299
|
}
|
380
300
|
return out;
|
381
301
|
}
|
302
|
+
async getDockerPortBindings(blockInstance, assetVersion) {
|
303
|
+
const bindHost = (0, utils_1.getBindHost)();
|
304
|
+
const ExposedPorts = {};
|
305
|
+
const addonEnv = {};
|
306
|
+
const PortBindings = {};
|
307
|
+
const portTypes = getProviderPorts(assetVersion);
|
308
|
+
let port = 80;
|
309
|
+
const promises = portTypes.map(async (portType) => {
|
310
|
+
const publicPort = await serviceManager_1.serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
311
|
+
const thisPort = port++; //TODO: Not sure how we should handle multiple ports or non-HTTP ports
|
312
|
+
const dockerPort = `${thisPort}/tcp`;
|
313
|
+
ExposedPorts[dockerPort] = {};
|
314
|
+
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = '' + thisPort;
|
315
|
+
PortBindings[dockerPort] = [
|
316
|
+
{
|
317
|
+
HostIp: bindHost,
|
318
|
+
HostPort: `${publicPort}`,
|
319
|
+
},
|
320
|
+
];
|
321
|
+
});
|
322
|
+
await Promise.all(promises);
|
323
|
+
return { PortBindings, ExposedPorts, addonEnv };
|
324
|
+
}
|
325
|
+
async ensureContainer(opts) {
|
326
|
+
const logs = new LogData_1.LogData();
|
327
|
+
const container = await containerManager_1.containerManager.ensureContainer(opts);
|
328
|
+
try {
|
329
|
+
if (opts.HealthCheck) {
|
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);
|
340
|
+
}
|
341
|
+
async _handleContainer(container, logs, deleteOnExit = false) {
|
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
|
+
});
|
369
|
+
return {
|
370
|
+
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
|
+
},
|
389
|
+
};
|
390
|
+
}
|
382
391
|
}
|
383
392
|
exports.BlockInstanceRunner = BlockInstanceRunner;
|
@@ -14,8 +14,6 @@ export declare class BlockInstanceRunner {
|
|
14
14
|
* Starts local process
|
15
15
|
*/
|
16
16
|
private _startLocalProcess;
|
17
|
-
private ensureContainer;
|
18
|
-
private _handleContainer;
|
19
17
|
private _startDockerProcess;
|
20
18
|
/**
|
21
19
|
*
|
@@ -27,4 +25,7 @@ export declare class BlockInstanceRunner {
|
|
27
25
|
* @private
|
28
26
|
*/
|
29
27
|
_startOperatorProcess(blockInstance: BlockProcessParams, blockUri: KapetaURI, providerDefinition: DefinitionInfo, env: StringMap): Promise<ProcessInfo>;
|
28
|
+
private getDockerPortBindings;
|
29
|
+
private ensureContainer;
|
30
|
+
private _handleContainer;
|
30
31
|
}
|
@@ -94,7 +94,7 @@ export class BlockInstanceRunner {
|
|
94
94
|
processInfo = await this._startLocalProcess(blockInstance, blockUri, env, assetVersion);
|
95
95
|
}
|
96
96
|
else {
|
97
|
-
processInfo = await this._startDockerProcess(blockInstance, blockUri, env);
|
97
|
+
processInfo = await this._startDockerProcess(blockInstance, blockUri, env, assetVersion);
|
98
98
|
}
|
99
99
|
if (portTypes.length > 0) {
|
100
100
|
processInfo.portType = portTypes[0];
|
@@ -132,26 +132,7 @@ export class BlockInstanceRunner {
|
|
132
132
|
const dockerOpts = localContainer.options ?? {};
|
133
133
|
const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
|
134
134
|
const workingDir = localContainer.workingDir ? localContainer.workingDir : '/workspace';
|
135
|
-
const
|
136
|
-
const ExposedPorts = {};
|
137
|
-
const addonEnv = {};
|
138
|
-
const PortBindings = {};
|
139
|
-
const portTypes = getProviderPorts(assetVersion);
|
140
|
-
let port = 80;
|
141
|
-
const promises = portTypes.map(async (portType) => {
|
142
|
-
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
143
|
-
const thisPort = port++; //TODO: Not sure how we should handle multiple ports or non-HTTP ports
|
144
|
-
const dockerPort = `${thisPort}/tcp`;
|
145
|
-
ExposedPorts[dockerPort] = {};
|
146
|
-
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = '' + thisPort;
|
147
|
-
PortBindings[dockerPort] = [
|
148
|
-
{
|
149
|
-
HostIp: bindHost,
|
150
|
-
HostPort: `${publicPort}`,
|
151
|
-
},
|
152
|
-
];
|
153
|
-
});
|
154
|
-
await Promise.all(promises);
|
135
|
+
const { PortBindings, ExposedPorts, addonEnv } = await this.getDockerPortBindings(blockInstance, assetVersion);
|
155
136
|
let HealthCheck = undefined;
|
156
137
|
if (localContainer.healthcheck) {
|
157
138
|
HealthCheck = containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
|
@@ -184,73 +165,7 @@ export class BlockInstanceRunner {
|
|
184
165
|
...dockerOpts,
|
185
166
|
});
|
186
167
|
}
|
187
|
-
async
|
188
|
-
const logs = new LogData();
|
189
|
-
const container = await containerManager.ensureContainer(opts);
|
190
|
-
try {
|
191
|
-
if (opts.HealthCheck) {
|
192
|
-
await containerManager.waitForHealthy(container);
|
193
|
-
}
|
194
|
-
else {
|
195
|
-
await containerManager.waitForReady(container);
|
196
|
-
}
|
197
|
-
}
|
198
|
-
catch (e) {
|
199
|
-
logs.addLog(e.message, 'ERROR');
|
200
|
-
}
|
201
|
-
return this._handleContainer(container, logs);
|
202
|
-
}
|
203
|
-
async _handleContainer(container, logs, deleteOnExit = false) {
|
204
|
-
let localContainer = container;
|
205
|
-
const logStream = (await container.logs({
|
206
|
-
follow: true,
|
207
|
-
stdout: true,
|
208
|
-
stderr: true,
|
209
|
-
tail: LogData.MAX_LINES,
|
210
|
-
}));
|
211
|
-
const outputEvents = new EventEmitter();
|
212
|
-
logStream.on('data', (data) => {
|
213
|
-
logs.addLog(data.toString());
|
214
|
-
outputEvents.emit('data', data);
|
215
|
-
});
|
216
|
-
logStream.on('error', (data) => {
|
217
|
-
logs.addLog(data.toString());
|
218
|
-
outputEvents.emit('data', data);
|
219
|
-
});
|
220
|
-
logStream.on('close', async () => {
|
221
|
-
const status = await container.status();
|
222
|
-
const data = status.data;
|
223
|
-
if (deleteOnExit) {
|
224
|
-
try {
|
225
|
-
await containerManager.remove(container);
|
226
|
-
}
|
227
|
-
catch (e) { }
|
228
|
-
}
|
229
|
-
outputEvents.emit('exit', data?.State?.ExitCode ?? 0);
|
230
|
-
});
|
231
|
-
return {
|
232
|
-
type: InstanceType.DOCKER,
|
233
|
-
pid: container.id,
|
234
|
-
output: outputEvents,
|
235
|
-
stop: async () => {
|
236
|
-
if (!localContainer) {
|
237
|
-
return;
|
238
|
-
}
|
239
|
-
try {
|
240
|
-
await localContainer.stop();
|
241
|
-
if (deleteOnExit) {
|
242
|
-
await containerManager.remove(localContainer);
|
243
|
-
}
|
244
|
-
}
|
245
|
-
catch (e) { }
|
246
|
-
localContainer = null;
|
247
|
-
},
|
248
|
-
logs: () => {
|
249
|
-
return logs.getLogs();
|
250
|
-
},
|
251
|
-
};
|
252
|
-
}
|
253
|
-
async _startDockerProcess(blockInstance, blockInfo, env) {
|
168
|
+
async _startDockerProcess(blockInstance, blockInfo, env, assetVersion) {
|
254
169
|
const { versionFile } = ClusterConfig.getRepositoryAssetInfoPath(blockInfo.handle, blockInfo.name, blockInfo.version);
|
255
170
|
const versionYml = versionFile;
|
256
171
|
if (!FS.existsSync(versionYml)) {
|
@@ -264,23 +179,28 @@ export class BlockInstanceRunner {
|
|
264
179
|
if (!dockerImage) {
|
265
180
|
throw new Error(`Missing docker image information: ${JSON.stringify(versionInfo?.artifact?.details)}`);
|
266
181
|
}
|
182
|
+
const { PortBindings, ExposedPorts, addonEnv } = await this.getDockerPortBindings(blockInstance, assetVersion);
|
267
183
|
const containerName = getBlockInstanceContainerName(blockInstance.id);
|
268
|
-
const logs = new LogData();
|
269
184
|
// For windows we need to default to root
|
270
185
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
271
186
|
return this.ensureContainer({
|
272
187
|
Image: dockerImage,
|
273
188
|
name: containerName,
|
189
|
+
ExposedPorts,
|
274
190
|
Labels: {
|
275
191
|
instance: blockInstance.id,
|
276
192
|
},
|
277
193
|
Env: [
|
278
194
|
...DOCKER_ENV_VARS,
|
279
195
|
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
280
|
-
...Object.entries(
|
196
|
+
...Object.entries({
|
197
|
+
...env,
|
198
|
+
...addonEnv
|
199
|
+
}).map(([key, value]) => `${key}=${value}`),
|
281
200
|
],
|
282
201
|
HostConfig: {
|
283
202
|
Binds: [`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`],
|
203
|
+
PortBindings,
|
284
204
|
},
|
285
205
|
});
|
286
206
|
}
|
@@ -373,4 +293,93 @@ export class BlockInstanceRunner {
|
|
373
293
|
}
|
374
294
|
return out;
|
375
295
|
}
|
296
|
+
async getDockerPortBindings(blockInstance, assetVersion) {
|
297
|
+
const bindHost = getBindHost();
|
298
|
+
const ExposedPorts = {};
|
299
|
+
const addonEnv = {};
|
300
|
+
const PortBindings = {};
|
301
|
+
const portTypes = getProviderPorts(assetVersion);
|
302
|
+
let port = 80;
|
303
|
+
const promises = portTypes.map(async (portType) => {
|
304
|
+
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
305
|
+
const thisPort = port++; //TODO: Not sure how we should handle multiple ports or non-HTTP ports
|
306
|
+
const dockerPort = `${thisPort}/tcp`;
|
307
|
+
ExposedPorts[dockerPort] = {};
|
308
|
+
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = '' + thisPort;
|
309
|
+
PortBindings[dockerPort] = [
|
310
|
+
{
|
311
|
+
HostIp: bindHost,
|
312
|
+
HostPort: `${publicPort}`,
|
313
|
+
},
|
314
|
+
];
|
315
|
+
});
|
316
|
+
await Promise.all(promises);
|
317
|
+
return { PortBindings, ExposedPorts, addonEnv };
|
318
|
+
}
|
319
|
+
async ensureContainer(opts) {
|
320
|
+
const logs = new LogData();
|
321
|
+
const container = await containerManager.ensureContainer(opts);
|
322
|
+
try {
|
323
|
+
if (opts.HealthCheck) {
|
324
|
+
await containerManager.waitForHealthy(container);
|
325
|
+
}
|
326
|
+
else {
|
327
|
+
await containerManager.waitForReady(container);
|
328
|
+
}
|
329
|
+
}
|
330
|
+
catch (e) {
|
331
|
+
logs.addLog(e.message, 'ERROR');
|
332
|
+
}
|
333
|
+
return this._handleContainer(container, logs);
|
334
|
+
}
|
335
|
+
async _handleContainer(container, logs, deleteOnExit = false) {
|
336
|
+
let localContainer = container;
|
337
|
+
const logStream = (await container.logs({
|
338
|
+
follow: true,
|
339
|
+
stdout: true,
|
340
|
+
stderr: true,
|
341
|
+
tail: LogData.MAX_LINES,
|
342
|
+
}));
|
343
|
+
const outputEvents = new EventEmitter();
|
344
|
+
logStream.on('data', (data) => {
|
345
|
+
logs.addLog(data.toString());
|
346
|
+
outputEvents.emit('data', data);
|
347
|
+
});
|
348
|
+
logStream.on('error', (data) => {
|
349
|
+
logs.addLog(data.toString());
|
350
|
+
outputEvents.emit('data', data);
|
351
|
+
});
|
352
|
+
logStream.on('close', async () => {
|
353
|
+
const status = await container.status();
|
354
|
+
const data = status.data;
|
355
|
+
if (deleteOnExit) {
|
356
|
+
try {
|
357
|
+
await containerManager.remove(container);
|
358
|
+
}
|
359
|
+
catch (e) { }
|
360
|
+
}
|
361
|
+
outputEvents.emit('exit', data?.State?.ExitCode ?? 0);
|
362
|
+
});
|
363
|
+
return {
|
364
|
+
type: InstanceType.DOCKER,
|
365
|
+
pid: container.id,
|
366
|
+
output: outputEvents,
|
367
|
+
stop: async () => {
|
368
|
+
if (!localContainer) {
|
369
|
+
return;
|
370
|
+
}
|
371
|
+
try {
|
372
|
+
await localContainer.stop();
|
373
|
+
if (deleteOnExit) {
|
374
|
+
await containerManager.remove(localContainer);
|
375
|
+
}
|
376
|
+
}
|
377
|
+
catch (e) { }
|
378
|
+
localContainer = null;
|
379
|
+
},
|
380
|
+
logs: () => {
|
381
|
+
return logs.getLogs();
|
382
|
+
},
|
383
|
+
};
|
384
|
+
}
|
376
385
|
}
|
package/package.json
CHANGED
@@ -117,7 +117,7 @@ export class BlockInstanceRunner {
|
|
117
117
|
if (blockUri.version === 'local') {
|
118
118
|
processInfo = await this._startLocalProcess(blockInstance, blockUri, env, assetVersion);
|
119
119
|
} else {
|
120
|
-
processInfo = await this._startDockerProcess(blockInstance, blockUri, env);
|
120
|
+
processInfo = await this._startDockerProcess(blockInstance, blockUri, env, assetVersion);
|
121
121
|
}
|
122
122
|
|
123
123
|
if (portTypes.length > 0) {
|
@@ -170,36 +170,16 @@ export class BlockInstanceRunner {
|
|
170
170
|
}
|
171
171
|
|
172
172
|
const containerName = getBlockInstanceContainerName(blockInstance.id);
|
173
|
-
|
174
173
|
const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
|
175
174
|
const dockerOpts = localContainer.options ?? {};
|
176
175
|
const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
|
177
176
|
const workingDir = localContainer.workingDir ? localContainer.workingDir : '/workspace';
|
178
177
|
|
179
|
-
const
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
const portTypes = getProviderPorts(assetVersion);
|
186
|
-
let port = 80;
|
187
|
-
const promises = portTypes.map(async (portType) => {
|
188
|
-
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
189
|
-
const thisPort = port++; //TODO: Not sure how we should handle multiple ports or non-HTTP ports
|
190
|
-
const dockerPort = `${thisPort}/tcp`;
|
191
|
-
ExposedPorts[dockerPort] = {};
|
192
|
-
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = '' + thisPort;
|
193
|
-
|
194
|
-
PortBindings[dockerPort] = [
|
195
|
-
{
|
196
|
-
HostIp: bindHost,
|
197
|
-
HostPort: `${publicPort}`,
|
198
|
-
},
|
199
|
-
];
|
200
|
-
});
|
201
|
-
|
202
|
-
await Promise.all(promises);
|
178
|
+
const {
|
179
|
+
PortBindings,
|
180
|
+
ExposedPorts,
|
181
|
+
addonEnv
|
182
|
+
} = await this.getDockerPortBindings(blockInstance, assetVersion);
|
203
183
|
|
204
184
|
let HealthCheck = undefined;
|
205
185
|
if (localContainer.healthcheck) {
|
@@ -235,83 +215,7 @@ export class BlockInstanceRunner {
|
|
235
215
|
});
|
236
216
|
}
|
237
217
|
|
238
|
-
private async
|
239
|
-
const logs = new LogData();
|
240
|
-
|
241
|
-
const container = await containerManager.ensureContainer(opts);
|
242
|
-
|
243
|
-
try {
|
244
|
-
if (opts.HealthCheck) {
|
245
|
-
await containerManager.waitForHealthy(container);
|
246
|
-
} else {
|
247
|
-
await containerManager.waitForReady(container);
|
248
|
-
}
|
249
|
-
} catch (e: any) {
|
250
|
-
logs.addLog(e.message, 'ERROR');
|
251
|
-
}
|
252
|
-
|
253
|
-
return this._handleContainer(container, logs);
|
254
|
-
}
|
255
|
-
|
256
|
-
private async _handleContainer(
|
257
|
-
container: Container,
|
258
|
-
logs: LogData,
|
259
|
-
deleteOnExit: boolean = false
|
260
|
-
): Promise<ProcessInfo> {
|
261
|
-
let localContainer: Container | null = container;
|
262
|
-
const logStream = (await container.logs({
|
263
|
-
follow: true,
|
264
|
-
stdout: true,
|
265
|
-
stderr: true,
|
266
|
-
tail: LogData.MAX_LINES,
|
267
|
-
})) as EventEmitter;
|
268
|
-
|
269
|
-
const outputEvents = new EventEmitter();
|
270
|
-
logStream.on('data', (data) => {
|
271
|
-
logs.addLog(data.toString());
|
272
|
-
outputEvents.emit('data', data);
|
273
|
-
});
|
274
|
-
|
275
|
-
logStream.on('error', (data) => {
|
276
|
-
logs.addLog(data.toString());
|
277
|
-
outputEvents.emit('data', data);
|
278
|
-
});
|
279
|
-
|
280
|
-
logStream.on('close', async () => {
|
281
|
-
const status = await container.status();
|
282
|
-
const data = status.data as any;
|
283
|
-
if (deleteOnExit) {
|
284
|
-
try {
|
285
|
-
await containerManager.remove(container);
|
286
|
-
} catch (e: any) {}
|
287
|
-
}
|
288
|
-
outputEvents.emit('exit', data?.State?.ExitCode ?? 0);
|
289
|
-
});
|
290
|
-
|
291
|
-
return {
|
292
|
-
type: InstanceType.DOCKER,
|
293
|
-
pid: container.id,
|
294
|
-
output: outputEvents,
|
295
|
-
stop: async () => {
|
296
|
-
if (!localContainer) {
|
297
|
-
return;
|
298
|
-
}
|
299
|
-
|
300
|
-
try {
|
301
|
-
await localContainer.stop();
|
302
|
-
if (deleteOnExit) {
|
303
|
-
await containerManager.remove(localContainer);
|
304
|
-
}
|
305
|
-
} catch (e) {}
|
306
|
-
localContainer = null;
|
307
|
-
},
|
308
|
-
logs: () => {
|
309
|
-
return logs.getLogs();
|
310
|
-
},
|
311
|
-
};
|
312
|
-
}
|
313
|
-
|
314
|
-
private async _startDockerProcess(blockInstance: BlockProcessParams, blockInfo: KapetaURI, env: StringMap) {
|
218
|
+
private async _startDockerProcess(blockInstance: BlockProcessParams, blockInfo: KapetaURI, env: StringMap, assetVersion: DefinitionInfo) {
|
315
219
|
const { versionFile } = ClusterConfig.getRepositoryAssetInfoPath(
|
316
220
|
blockInfo.handle,
|
317
221
|
blockInfo.name,
|
@@ -333,8 +237,13 @@ export class BlockInstanceRunner {
|
|
333
237
|
throw new Error(`Missing docker image information: ${JSON.stringify(versionInfo?.artifact?.details)}`);
|
334
238
|
}
|
335
239
|
|
240
|
+
const {
|
241
|
+
PortBindings,
|
242
|
+
ExposedPorts,
|
243
|
+
addonEnv
|
244
|
+
} = await this.getDockerPortBindings(blockInstance, assetVersion);
|
245
|
+
|
336
246
|
const containerName = getBlockInstanceContainerName(blockInstance.id);
|
337
|
-
const logs = new LogData();
|
338
247
|
|
339
248
|
// For windows we need to default to root
|
340
249
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
@@ -342,16 +251,22 @@ export class BlockInstanceRunner {
|
|
342
251
|
return this.ensureContainer({
|
343
252
|
Image: dockerImage,
|
344
253
|
name: containerName,
|
254
|
+
ExposedPorts,
|
345
255
|
Labels: {
|
346
256
|
instance: blockInstance.id,
|
347
257
|
},
|
348
258
|
Env: [
|
349
259
|
...DOCKER_ENV_VARS,
|
350
260
|
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
351
|
-
...Object.entries(
|
261
|
+
...Object.entries({
|
262
|
+
...env,
|
263
|
+
...addonEnv
|
264
|
+
}).map(([key, value]) => `${key}=${value}`),
|
265
|
+
|
352
266
|
],
|
353
267
|
HostConfig: {
|
354
268
|
Binds: [`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`],
|
269
|
+
PortBindings,
|
355
270
|
},
|
356
271
|
});
|
357
272
|
}
|
@@ -471,4 +386,110 @@ export class BlockInstanceRunner {
|
|
471
386
|
|
472
387
|
return out;
|
473
388
|
}
|
389
|
+
|
390
|
+
|
391
|
+
private async getDockerPortBindings(blockInstance: BlockProcessParams, assetVersion: DefinitionInfo) {
|
392
|
+
const bindHost = getBindHost();
|
393
|
+
const ExposedPorts: AnyMap = {};
|
394
|
+
const addonEnv: StringMap = {};
|
395
|
+
const PortBindings: AnyMap = {};
|
396
|
+
|
397
|
+
const portTypes = getProviderPorts(assetVersion);
|
398
|
+
let port = 80;
|
399
|
+
const promises = portTypes.map(async (portType) => {
|
400
|
+
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
401
|
+
const thisPort = port++; //TODO: Not sure how we should handle multiple ports or non-HTTP ports
|
402
|
+
const dockerPort = `${thisPort}/tcp`;
|
403
|
+
ExposedPorts[dockerPort] = {};
|
404
|
+
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = '' + thisPort;
|
405
|
+
|
406
|
+
PortBindings[dockerPort] = [
|
407
|
+
{
|
408
|
+
HostIp: bindHost,
|
409
|
+
HostPort: `${publicPort}`,
|
410
|
+
},
|
411
|
+
];
|
412
|
+
});
|
413
|
+
|
414
|
+
|
415
|
+
await Promise.all(promises);
|
416
|
+
|
417
|
+
return {PortBindings,ExposedPorts, addonEnv};
|
418
|
+
}
|
419
|
+
|
420
|
+
private async ensureContainer(opts: any) {
|
421
|
+
const logs = new LogData();
|
422
|
+
|
423
|
+
const container = await containerManager.ensureContainer(opts);
|
424
|
+
|
425
|
+
try {
|
426
|
+
if (opts.HealthCheck) {
|
427
|
+
await containerManager.waitForHealthy(container);
|
428
|
+
} else {
|
429
|
+
await containerManager.waitForReady(container);
|
430
|
+
}
|
431
|
+
} catch (e: any) {
|
432
|
+
logs.addLog(e.message, 'ERROR');
|
433
|
+
}
|
434
|
+
|
435
|
+
return this._handleContainer(container, logs);
|
436
|
+
}
|
437
|
+
|
438
|
+
private async _handleContainer(
|
439
|
+
container: Container,
|
440
|
+
logs: LogData,
|
441
|
+
deleteOnExit: boolean = false
|
442
|
+
): Promise<ProcessInfo> {
|
443
|
+
let localContainer: Container | null = container;
|
444
|
+
const logStream = (await container.logs({
|
445
|
+
follow: true,
|
446
|
+
stdout: true,
|
447
|
+
stderr: true,
|
448
|
+
tail: LogData.MAX_LINES,
|
449
|
+
})) as EventEmitter;
|
450
|
+
|
451
|
+
const outputEvents = new EventEmitter();
|
452
|
+
logStream.on('data', (data) => {
|
453
|
+
logs.addLog(data.toString());
|
454
|
+
outputEvents.emit('data', data);
|
455
|
+
});
|
456
|
+
|
457
|
+
logStream.on('error', (data) => {
|
458
|
+
logs.addLog(data.toString());
|
459
|
+
outputEvents.emit('data', data);
|
460
|
+
});
|
461
|
+
|
462
|
+
logStream.on('close', async () => {
|
463
|
+
const status = await container.status();
|
464
|
+
const data = status.data as any;
|
465
|
+
if (deleteOnExit) {
|
466
|
+
try {
|
467
|
+
await containerManager.remove(container);
|
468
|
+
} catch (e: any) {}
|
469
|
+
}
|
470
|
+
outputEvents.emit('exit', data?.State?.ExitCode ?? 0);
|
471
|
+
});
|
472
|
+
|
473
|
+
return {
|
474
|
+
type: InstanceType.DOCKER,
|
475
|
+
pid: container.id,
|
476
|
+
output: outputEvents,
|
477
|
+
stop: async () => {
|
478
|
+
if (!localContainer) {
|
479
|
+
return;
|
480
|
+
}
|
481
|
+
|
482
|
+
try {
|
483
|
+
await localContainer.stop();
|
484
|
+
if (deleteOnExit) {
|
485
|
+
await containerManager.remove(localContainer);
|
486
|
+
}
|
487
|
+
} catch (e) {}
|
488
|
+
localContainer = null;
|
489
|
+
},
|
490
|
+
logs: () => {
|
491
|
+
return logs.getLogs();
|
492
|
+
},
|
493
|
+
};
|
494
|
+
}
|
474
495
|
}
|