@kapeta/local-cluster-service 0.2.1 → 0.4.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 CHANGED
@@ -1,3 +1,17 @@
1
+ # [0.4.0](https://github.com/kapetacom/local-cluster-service/compare/v0.3.0...v0.4.0) (2023-06-01)
2
+
3
+
4
+ ### Features
5
+
6
+ * Change to always run local code in docker container ([#25](https://github.com/kapetacom/local-cluster-service/issues/25)) ([6e4021e](https://github.com/kapetacom/local-cluster-service/commit/6e4021e67968467555f1043f2972fc7a877aa3b7))
7
+
8
+ # [0.3.0](https://github.com/kapetacom/local-cluster-service/compare/v0.2.1...v0.3.0) (2023-05-08)
9
+
10
+
11
+ ### Features
12
+
13
+ * load docker config from clusterConfig if available [KAP-609] ([1ced2c1](https://github.com/kapetacom/local-cluster-service/commit/1ced2c1ed2f72bf3331e558ee2c685385c89ab1f))
14
+
1
15
  ## [0.2.1](https://github.com/kapetacom/local-cluster-service/compare/v0.2.0...v0.2.1) (2023-05-07)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -22,7 +22,7 @@
22
22
  "homepage": "https://github.com/kapetacom/local-cluster-service#readme",
23
23
  "dependencies": {
24
24
  "@kapeta/codegen": "<2",
25
- "@kapeta/local-cluster-config": "<2",
25
+ "@kapeta/local-cluster-config": ">= 0.1.0 <2",
26
26
  "@kapeta/nodejs-api-client": "<2",
27
27
  "@kapeta/nodejs-registry-utils": "<2",
28
28
  "@kapeta/nodejs-utils": "<2",
@@ -44,6 +44,10 @@
44
44
  "devDependencies": {
45
45
  "nodemon": "^2.0.2"
46
46
  },
47
+ "prettier": {
48
+ "tabWidth": 4,
49
+ "singleQuote": true
50
+ },
47
51
  "release": {
48
52
  "plugins": [
49
53
  "@semantic-release/commit-analyzer",
@@ -99,9 +99,7 @@ router.get('/identity', async (req, res) => {
99
99
 
100
100
  res.send(identity);
101
101
  } catch(err) {
102
- console.log(err);
103
-
104
- res.send({error: err.message});
102
+ res.status(400).send({error: err.message});
105
103
  }
106
104
  });
107
105
 
@@ -92,7 +92,7 @@ class ConfigManager {
92
92
  throw new Error(`No uses of block "${blockRef}" was found in plan: "${systemId}"`)
93
93
  }
94
94
 
95
- throw new Error(`No uses of block "${blockRef}" was found any known plan`);
95
+ throw new Error(`No uses of block "${blockRef}" was found in any known plan`);
96
96
  }
97
97
 
98
98
  if (matchingIdentities.length > 1) {
@@ -1,13 +1,15 @@
1
- const {Docker} = require("node-docker-api");
2
- const path = require("path");
1
+ const { Docker } = require('node-docker-api');
2
+ const path = require('path');
3
3
  const _ = require('lodash');
4
- const FS = require("node:fs");
5
- const os = require("os");
6
- const Path = require("path");
7
- const storageService = require("./storageService");
8
- const mkdirp = require("mkdirp");
9
- const {parseKapetaUri} = require("@kapeta/nodejs-utils");
10
- const LABEL_PORT_PREFIX = "kapeta_port-";
4
+ const os = require('os');
5
+ const Path = require('path');
6
+ const storageService = require('./storageService');
7
+ const mkdirp = require('mkdirp');
8
+ const { parseKapetaUri } = require('@kapeta/nodejs-utils');
9
+
10
+ const ClusterConfiguration = require('@kapeta/local-cluster-config');
11
+
12
+ const LABEL_PORT_PREFIX = 'kapeta_port-';
11
13
 
12
14
  const NANO_SECOND = 1000000;
13
15
  const HEALTH_CHECK_INTERVAL = 2000;
@@ -15,9 +17,9 @@ const HEALTH_CHECK_MAX = 20;
15
17
 
16
18
  const promisifyStream = (stream) =>
17
19
  new Promise((resolve, reject) => {
18
- stream.on("data", (d) => console.log(d.toString()));
19
- stream.on("end", resolve);
20
- stream.on("error", reject);
20
+ stream.on('data', (d) => console.log(d.toString()));
21
+ stream.on('end', resolve);
22
+ stream.on('error', reject);
21
23
  });
22
24
 
23
25
  class ContainerManager {
@@ -27,8 +29,41 @@ class ContainerManager {
27
29
  this._mountDir = Path.join(storageService.getKapetaBasedir(), 'mounts');
28
30
  mkdirp.sync(this._mountDir);
29
31
  }
30
-
31
-
32
+
33
+ async initialize() {
34
+ // Use the value from cluster-service.yml if configured
35
+ const dockerConfig = ClusterConfiguration.getDockerConfig();
36
+ const connectOptions =
37
+ Object.keys(dockerConfig).length > 0
38
+ ? [dockerConfig]
39
+ : [
40
+ // use defaults: DOCKER_HOST etc from env, if available
41
+ undefined,
42
+ // default linux
43
+ { socketPath: '/var/run/docker.sock' },
44
+ // default macOS
45
+ {
46
+ socketPath: path.join(
47
+ os.homedir(),
48
+ '.docker/run/docker.sock'
49
+ ),
50
+ },
51
+ // Default http
52
+ { protocol: 'http', host: 'localhost', port: 2375 },
53
+ { protocol: 'https', host: 'localhost', port: 2376 },
54
+ ];
55
+ for (const opts of connectOptions) {
56
+ try {
57
+ const client = new Docker(opts);
58
+ await client.ping();
59
+ this._docker = client;
60
+ this._alive = true;
61
+ return;
62
+ } catch (err) {
63
+ // silently ignore bad configs
64
+ }
65
+ }
66
+ }
32
67
 
33
68
  isAlive() {
34
69
  return this._alive;
@@ -36,9 +71,14 @@ class ContainerManager {
36
71
 
37
72
  getMountPoint(kind, mountName) {
38
73
  const kindUri = parseKapetaUri(kind);
39
- return Path.join(this._mountDir, kindUri.handle, kindUri.name, mountName);
74
+ return Path.join(
75
+ this._mountDir,
76
+ kindUri.handle,
77
+ kindUri.name,
78
+ mountName
79
+ );
40
80
  }
41
-
81
+
42
82
  createMounts(kind, mountOpts) {
43
83
  const mounts = {};
44
84
 
@@ -50,42 +90,16 @@ class ContainerManager {
50
90
  return mounts;
51
91
  }
52
92
 
53
- async initialize() {
54
- // try
55
- const connectOptions = [
56
- // use defaults: DOCKER_HOST etc from env, if available
57
- undefined,
58
- // default linux
59
- {socketPath: "/var/run/docker.sock"},
60
- // default macOS
61
- {socketPath: path.join(os.homedir(), ".docker/run/docker.sock")},
62
- // Default http
63
- {protocol: "http", host: "localhost", port: 2375},
64
- {protocol: "https", host: "localhost", port: 2376},
65
- ];
66
- for (const opts of connectOptions) {
67
- try {
68
- const client = new Docker(opts);
69
- await client.ping();
70
- this._docker = client;
71
- this._alive = true;
72
- return;
73
- } catch (err) {
74
- // silently ignore bad configs
75
- }
76
- }
77
- throw new Error("Unable to connect to docker");
78
- }
79
-
80
93
  async ping() {
81
-
82
94
  try {
83
95
  const pingResult = await this.docker().ping();
84
96
  if (pingResult !== 'OK') {
85
97
  throw new Error(`Ping failed: ${pingResult}`);
86
98
  }
87
99
  } catch (e) {
88
- throw new Error(`Docker not running. Please start the docker daemon before running this command. Error: ${e.message}`);
100
+ throw new Error(
101
+ `Docker not running. Please start the docker daemon before running this command. Error: ${e.message}`
102
+ );
89
103
  }
90
104
  }
91
105
  docker() {
@@ -96,8 +110,8 @@ class ContainerManager {
96
110
  }
97
111
 
98
112
  async getContainerByName(containerName) {
99
- const containers = await this.docker().container.list({all: true});
100
- return containers.find(container => {
113
+ const containers = await this.docker().container.list({ all: true });
114
+ return containers.find((container) => {
101
115
  return container.data.Names.indexOf(`/${containerName}`) > -1;
102
116
  });
103
117
  }
@@ -108,8 +122,8 @@ class ContainerManager {
108
122
  tag = 'latest';
109
123
  }
110
124
 
111
- await this.docker().image
112
- .create(
125
+ await this.docker()
126
+ .image.create(
113
127
  {},
114
128
  {
115
129
  fromImage: imageName,
@@ -118,25 +132,25 @@ class ContainerManager {
118
132
  )
119
133
  .then((stream) => promisifyStream(stream));
120
134
  }
121
-
135
+
122
136
  toDockerMounts(mounts) {
123
- const Mounts = [];
137
+ const Mounts = [];
124
138
  _.forEach(mounts, (Source, Target) => {
125
139
  Mounts.push({
126
140
  Target,
127
141
  Source,
128
- Type: "bind",
142
+ Type: 'bind',
129
143
  ReadOnly: false,
130
- Consistency: "consistent",
144
+ Consistency: 'consistent',
131
145
  });
132
146
  });
133
-
147
+
134
148
  return Mounts;
135
149
  }
136
-
150
+
137
151
  toDockerHealth(health) {
138
152
  return {
139
- Test: ["CMD-SHELL", health.cmd],
153
+ Test: ['CMD-SHELL', health.cmd],
140
154
  Interval: health.interval
141
155
  ? health.interval * NANO_SECOND
142
156
  : 5000 * NANO_SECOND,
@@ -155,25 +169,24 @@ class ContainerManager {
155
169
  * @return {Promise<ContainerInfo>}
156
170
  */
157
171
  async run(image, name, opts) {
158
-
159
172
  const PortBindings = {};
160
173
  const Env = [];
161
174
  const Labels = {
162
- kapeta: "true",
175
+ kapeta: 'true',
163
176
  };
164
177
 
165
- console.log("Pulling image: %s", image);
178
+ console.log('Pulling image: %s', image);
166
179
 
167
180
  await this.pull(image);
168
181
 
169
- console.log("Image pulled: %s", image);
182
+ console.log('Image pulled: %s', image);
170
183
 
171
184
  _.forEach(opts.ports, (portInfo, containerPort) => {
172
185
  PortBindings['' + containerPort] = [
173
186
  {
174
187
  HostPort: '' + portInfo.hostPort,
175
- HostIp: '127.0.0.1'
176
- }
188
+ HostIp: '127.0.0.1',
189
+ },
177
190
  ];
178
191
 
179
192
  Labels[LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
@@ -182,7 +195,7 @@ class ContainerManager {
182
195
  const Mounts = this.toDockerMounts(opts.mounts);
183
196
 
184
197
  _.forEach(opts.env, (value, name) => {
185
- Env.push(name + "=" + value);
198
+ Env.push(name + '=' + value);
186
199
  });
187
200
 
188
201
  let HealthCheck = undefined;
@@ -190,10 +203,9 @@ class ContainerManager {
190
203
  if (opts.health) {
191
204
  HealthCheck = this.toDockerHealth(opts.health);
192
205
 
193
- console.log("Adding health check", HealthCheck);
206
+ console.log('Adding health check', HealthCheck);
194
207
  }
195
208
 
196
-
197
209
  const dockerContainer = await this.startContainer({
198
210
  name: name,
199
211
  Image: image,
@@ -202,8 +214,8 @@ class ContainerManager {
202
214
  HealthCheck,
203
215
  HostConfig: {
204
216
  PortBindings,
205
- Mounts
206
- }
217
+ Mounts,
218
+ },
207
219
  });
208
220
 
209
221
  if (opts.health) {
@@ -215,12 +227,36 @@ class ContainerManager {
215
227
 
216
228
  async startContainer(opts) {
217
229
  const dockerContainer = await this.docker().container.create(opts);
218
-
219
230
  await dockerContainer.start();
220
-
221
231
  return dockerContainer;
222
232
  }
223
233
 
234
+ async waitForReady(container, attempt) {
235
+ if (!attempt) {
236
+ attempt = 0;
237
+ }
238
+
239
+ if (attempt >= HEALTH_CHECK_MAX) {
240
+ throw new Error(
241
+ 'Container did not become ready within the timeout'
242
+ );
243
+ }
244
+
245
+ if (await this._isReady(container)) {
246
+ return;
247
+ }
248
+
249
+ return new Promise((resolve, reject) => {
250
+ setTimeout(async () => {
251
+ try {
252
+ await this.waitForReady(container, attempt + 1);
253
+ resolve();
254
+ } catch (err) {
255
+ reject(err);
256
+ }
257
+ }, HEALTH_CHECK_INTERVAL);
258
+ });
259
+ }
224
260
 
225
261
  async waitForHealthy(container, attempt) {
226
262
  if (!attempt) {
@@ -228,7 +264,9 @@ class ContainerManager {
228
264
  }
229
265
 
230
266
  if (attempt >= HEALTH_CHECK_MAX) {
231
- throw new Error("Container did not become healthy within the timeout");
267
+ throw new Error(
268
+ 'Container did not become healthy within the timeout'
269
+ );
232
270
  }
233
271
 
234
272
  if (await this._isHealthy(container)) {
@@ -247,9 +285,16 @@ class ContainerManager {
247
285
  });
248
286
  }
249
287
 
288
+ async _isReady(container) {
289
+ const info = await container.status();
290
+ if (info?.data?.State?.Status === 'exited') {
291
+ throw new Error('Container exited unexpectedly');
292
+ }
293
+ return info?.data?.State?.Running;
294
+ }
250
295
  async _isHealthy(container) {
251
296
  const info = await container.status();
252
- return info?.data?.State?.Health?.Status === "healthy";
297
+ return info?.data?.State?.Health?.Status === 'healthy';
253
298
  }
254
299
 
255
300
  /**
@@ -265,7 +310,6 @@ class ContainerManager {
265
310
  await dockerContainer.status();
266
311
  } catch (err) {
267
312
  //Ignore
268
- console.log("Container not available - creating it: %s", name);
269
313
  dockerContainer = null;
270
314
  }
271
315
 
@@ -314,7 +358,7 @@ class ContainerInfo {
314
358
  }
315
359
 
316
360
  async remove(opts) {
317
- await this._container.delete({force: !!opts.force});
361
+ await this._container.delete({ force: !!opts.force });
318
362
  }
319
363
 
320
364
  async getPort(type) {
@@ -393,6 +393,7 @@ class InstanceManager {
393
393
 
394
394
  return this._processes[planRef][instanceId] = process;
395
395
  } catch (e) {
396
+ console.warn('Failed to start instance', e);
396
397
  const logs = [
397
398
  {
398
399
  source: "stdout",
@@ -27,6 +27,19 @@ const DOCKER_ENV_VARS = [
27
27
  ]
28
28
 
29
29
 
30
+ function getProvider(uri) {
31
+ return ClusterConfig.getProviderDefinitions().find(provider => {
32
+ const ref = `${provider.definition.metadata.name}:${provider.version}`
33
+ return parseKapetaUri(ref).id === uri.id;
34
+ });
35
+ }
36
+
37
+ function getProviderPorts(assetVersion) {
38
+ return assetVersion.definition?.spec?.providers.map(provider => {
39
+ return provider.spec?.port?.type
40
+ }).filter(t => !!t) ?? [];
41
+ }
42
+
30
43
  class BlockInstanceRunner {
31
44
  /**
32
45
  * @param {string} [planReference]
@@ -66,7 +79,7 @@ class BlockInstanceRunner {
66
79
  * @private
67
80
  */
68
81
  async _execute(blockInstance) {
69
- const env = Object.assign({}, process.env);
82
+ const env = {};
70
83
 
71
84
  if (this._systemId) {
72
85
  env[KAPETA_SYSTEM_ID] = this._systemId;
@@ -86,23 +99,20 @@ class BlockInstanceRunner {
86
99
  blockUri.version = 'local';
87
100
  }
88
101
 
89
- const definition = ClusterConfig.getDefinitions().find(definitions => {
102
+ const assetVersion = ClusterConfig.getDefinitions().find(definitions => {
90
103
  const ref = `${definitions.definition.metadata.name}:${definitions.version}`
91
104
  return parseKapetaUri(ref).id === blockUri.id;
92
105
  });
93
106
 
94
- if (!definition) {
107
+ if (!assetVersion) {
95
108
  throw new Error(`Block definition not found: ${blockUri.id}`);
96
109
  }
97
110
 
98
- const kindUri = parseKapetaUri(definition.definition.kind);
111
+ const kindUri = parseKapetaUri(assetVersion.definition.kind);
99
112
 
100
- const provider = ClusterConfig.getProviderDefinitions().find(provider => {
101
- const ref = `${provider.definition.metadata.name}:${provider.version}`
102
- return parseKapetaUri(ref).id === kindUri.id;
103
- });
113
+ const providerVersion = getProvider(kindUri);
104
114
 
105
- if (!provider) {
115
+ if (!providerVersion) {
106
116
  throw new Error(`Kind not found: ${kindUri.id}`);
107
117
  }
108
118
 
@@ -111,15 +121,14 @@ class BlockInstanceRunner {
111
121
  */
112
122
  let processDetails;
113
123
 
114
- if (provider.definition.kind === KIND_BLOCK_TYPE_OPERATOR) {
115
- processDetails = await this._startOperatorProcess(blockInstance, blockUri, provider, env);
124
+ if (providerVersion.definition.kind === KIND_BLOCK_TYPE_OPERATOR) {
125
+ processDetails = await this._startOperatorProcess(blockInstance, blockUri, providerVersion, env);
116
126
  } else {
117
127
  //We need a port type to know how to connect to the block consistently
118
- const portTypes = definition.definition?.spec?.providers.map(provider => {
119
- return provider.spec?.port?.type
120
- }).filter(t => !!t) ?? [];
128
+ const portTypes = getProviderPorts(assetVersion);
129
+
121
130
  if (blockUri.version === 'local') {
122
- processDetails = await this._startLocalProcess(blockInstance, blockUri, env);
131
+ processDetails = await this._startLocalProcess(blockInstance, blockUri, env, assetVersion);
123
132
  } else {
124
133
  processDetails = await this._startDockerProcess(blockInstance, blockUri, env);
125
134
  }
@@ -141,10 +150,11 @@ class BlockInstanceRunner {
141
150
  * @param {BlockInstanceInfo} blockInstance
142
151
  * @param {BlockInfo} blockInfo
143
152
  * @param {EnvironmentVariables} env
153
+ * @param assetVersion
144
154
  * @return {ProcessDetails}
145
155
  * @private
146
156
  */
147
- _startLocalProcess(blockInstance, blockInfo, env) {
157
+ async _startLocalProcess(blockInstance, blockInfo, env, assetVersion) {
148
158
  const baseDir = ClusterConfig.getRepositoryAssetPath(
149
159
  blockInfo.handle,
150
160
  blockInfo.name,
@@ -158,61 +168,125 @@ class BlockInstanceRunner {
158
168
  );
159
169
  }
160
170
 
161
- const startScript = Path.resolve(baseDir, 'scripts/start.sh');
162
- if (!FS.existsSync(startScript)) {
163
- throw new Error(
164
- `Start script did not exist for local block.\n` +
165
- `Expected runnable start script here: ${startScript}`
166
- )
171
+ const kindUri = parseKapetaUri(assetVersion.definition.spec.target.kind);
172
+
173
+ const targetVersion = getProvider(kindUri);
174
+
175
+ if (!targetVersion) {
176
+ throw new Error(`Target not found: ${kindUri.id}`);
167
177
  }
168
178
 
179
+ const localContainer = targetVersion.definition.spec.local;
180
+
181
+ if (!localContainer) {
182
+ throw new Error(`Missing local container information from target: ${kindUri.id}`);
183
+ }
184
+
185
+ const dockerImage = localContainer.image;
186
+ if (!dockerImage) {
187
+ throw new Error(`Missing docker image information: ${JSON.stringify(localContainer)}`);
188
+ }
189
+
190
+ const containerName = `kapeta-block-instance-${blockInstance.id}`;
169
191
  const logs = new LogData();
170
- const childProcess = spawn(startScript, [], {
171
- cwd: baseDir,
172
- env,
173
- detached: true,
174
- stdio: [
175
- 'pipe', 'pipe', 'pipe'
176
- ]
177
- });
192
+ logs.addLog(`Starting block ${blockInstance.ref}`);
193
+ let container = await containerManager.getContainerByName(containerName);
194
+ console.log('Starting dev container', containerName);
178
195
 
179
- logs.addLog(`Starting block ${blockInstance.ref} using script ${startScript}`);
180
- const outputEvents = new EventEmitter();
181
- /**
182
- *
183
- * @type {ProcessDetails}
184
- */
185
- const out = {
186
- type: 'local',
187
- pid: childProcess.pid,
188
- output: outputEvents,
189
- stderr: childProcess.stderr,
190
- logs: () => {
191
- return logs.getLogs();
192
- },
193
- stop: () => {
194
- childProcess.kill('SIGTERM');
196
+ if (container) {
197
+ console.log(`Container already exists. Deleting...`);
198
+ try {
199
+ await container.delete({
200
+ force: true
201
+ })
202
+ } catch (e) {
203
+ throw new Error('Failed to delete existing container: ' + e.message);
195
204
  }
196
- };
205
+ container = null;
206
+ }
197
207
 
198
- childProcess.stdout.on('data', (data) => {
199
- logs.addLog(data.toString());
200
- outputEvents.emit('data', data);
201
- });
208
+ logs.addLog(`Creating new container for block: ${containerName}`);
209
+ console.log('Creating new dev container', containerName, dockerImage);
210
+ await containerManager.pull(dockerImage);
211
+
212
+ const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
213
+ const dockerOpts = localContainer.options ?? {};
214
+ const homeDir = localContainer.homeDir ? localContainer.homeDir : '/root';
215
+ const workingDir = localContainer.workingDir ? localContainer.workingDir : '/workspace';
216
+
217
+ const ExposedPorts = {};
218
+ const addonEnv = {};
219
+ const PortBindings = {};
220
+
221
+ const portTypes = getProviderPorts(assetVersion);
222
+ let port = 80;
223
+ const promises = portTypes
224
+ .map(async (portType) => {
225
+ const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
226
+ const thisPort = port++; //TODO: Not sure how we should handle multiple ports or non-HTTP ports
227
+ const dockerPort = `${thisPort}/tcp`;
228
+ ExposedPorts[dockerPort] = {};
229
+ addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = thisPort;
230
+
231
+ PortBindings[dockerPort] = [
232
+ {
233
+ HostIp: "127.0.0.1", //No public
234
+ HostPort: `${publicPort}`
235
+ }
236
+ ];
237
+ });
202
238
 
203
- childProcess.stderr.on('data', (data) => {
204
- logs.addLog(data.toString());
205
- outputEvents.emit('data', data);
206
- });
239
+ await Promise.all(promises);
207
240
 
208
- childProcess.on('exit', (code) => {
209
- logs.addLog(`Block ${blockInstance.ref} exited with code: ${code}`);
210
- outputEvents.emit('exit', code);
241
+ let HealthCheck = undefined;
242
+ if (localContainer.healthcheck) {
243
+ HealthCheck = containerManager.toDockerHealth({cmd: localContainer.healthcheck});
244
+ }
245
+
246
+ container = await containerManager.startContainer({
247
+ Image: dockerImage,
248
+ name: containerName,
249
+ WorkingDir: workingDir,
250
+ Labels: {
251
+ 'instance': blockInstance.id
252
+ },
253
+ HealthCheck,
254
+ ExposedPorts,
255
+ Cmd: startCmd ? startCmd.split(/\s+/g) : [],
256
+ Env: [
257
+ ...DOCKER_ENV_VARS,
258
+ ...Object.entries({
259
+ ...env,
260
+ ...addonEnv
261
+ }).map(([key, value]) => `${key}=${value}`)
262
+ ],
263
+ HostConfig: {
264
+ Binds: [
265
+ `${ClusterConfig.getKapetaBasedir()}:${homeDir}/.kapeta`,
266
+ `${baseDir}:${workingDir}` //We mount
267
+ ],
268
+ PortBindings
269
+ },
270
+ ...dockerOpts
211
271
  });
212
272
 
213
- return out;
273
+ if (HealthCheck) {
274
+ await containerManager.waitForHealthy(container);
275
+ } else {
276
+ await containerManager.waitForReady(container);
277
+ }
278
+
279
+ return this._handleContainer(container, logs);
214
280
  }
215
281
 
282
+ /**
283
+ *
284
+ * @param container
285
+ * @param logs
286
+ * @param deleteOnExit
287
+ * @return {Promise<ProcessDetails>}
288
+ * @private
289
+ */
216
290
  async _handleContainer(container, logs , deleteOnExit = false) {
217
291
  const logStream = await container.logs({
218
292
  follow: true,
@@ -315,22 +389,36 @@ class BlockInstanceRunner {
315
389
  container = await containerManager.startContainer({
316
390
  Image: dockerImage,
317
391
  name: containerName,
318
- Binds: [
319
- `${ClusterConfig.getKapetaBasedir()}:${ClusterConfig.getKapetaBasedir()}`
320
- ],
321
392
  Labels: {
322
393
  'instance': blockInstance.id
323
394
  },
324
395
  Env: [
325
396
  ...DOCKER_ENV_VARS,
326
397
  ...Object.entries(env).map(([key, value]) => `${key}=${value}`)
327
- ]
398
+ ],
399
+ HostConfig: {
400
+ Binds: [
401
+ `${ClusterConfig.getKapetaBasedir()}:${ClusterConfig.getKapetaBasedir()}`
402
+ ],
403
+
404
+ }
328
405
  });
406
+
407
+ await containerManager.waitForReady(container);
329
408
  }
330
409
 
331
410
  return this._handleContainer(container, logs);
332
411
  }
333
412
 
413
+ /**
414
+ *
415
+ * @param blockInstance
416
+ * @param blockUri
417
+ * @param providerDefinition
418
+ * @param {{[key:string]:string}} env
419
+ * @return {Promise<ProcessDetails>}
420
+ * @private
421
+ */
334
422
  async _startOperatorProcess(blockInstance, blockUri, providerDefinition, env) {
335
423
  const {assetFile} = ClusterConfig.getRepositoryAssetInfoPath(
336
424
  blockUri.handle,
@@ -453,6 +541,8 @@ class BlockInstanceRunner {
453
541
 
454
542
  if (HealthCheck) {
455
543
  await containerManager.waitForHealthy(container);
544
+ } else {
545
+ await containerManager.waitForReady(container);
456
546
  }
457
547
  }
458
548