@kapeta/local-cluster-service 0.0.76 → 0.1.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,10 @@
1
+ # [0.1.0](https://github.com/kapetacom/local-cluster-service/compare/v0.0.76...v0.1.0) (2023-05-06)
2
+
3
+
4
+ ### Features
5
+
6
+ * Allow running docker block operators ([0a3992c](https://github.com/kapetacom/local-cluster-service/commit/0a3992c359a119a623ed7d0423e6f7ad814aa8d3))
7
+
1
8
  ## [0.0.76](https://github.com/kapetacom/local-cluster-service/compare/v0.0.75...v0.0.76) (2023-05-06)
2
9
 
3
10
 
package/definitions.d.ts CHANGED
@@ -23,8 +23,8 @@ declare function ProxyRequestHandler(req:Request, res:Response, info:ProxyReques
23
23
 
24
24
  interface Connection {
25
25
  mapping: any
26
- from: ResourceRef
27
- to: ResourceRef
26
+ provider: ResourceRef
27
+ consumer: ResourceRef
28
28
  }
29
29
 
30
30
  interface ResourceInfo {
@@ -36,7 +36,7 @@ interface ResourceInfo {
36
36
  interface ProxyRequestInfo {
37
37
  address: string
38
38
  connection:Connection
39
- fromResource:ResourceInfo
40
- toResource:ResourceInfo
39
+ providerResource:ResourceInfo
40
+ consumerResource:ResourceInfo
41
41
  consumerPath:string
42
42
  }
package/index.js CHANGED
@@ -20,7 +20,7 @@ function createServer() {
20
20
  app.use('/assets', require('./src/assets/routes'));
21
21
  app.use('/providers', require('./src/providers/routes'));
22
22
  app.use('/', (err, req, res, next) => {
23
- console.error('Request failed: %s %s', req.method, req.originalUrl, err.stack);
23
+ console.error('Request failed: %s %s', req.method, req.originalUrl, err);
24
24
  res.status(500).send({
25
25
  ok: false,
26
26
  error: err.error ?? err.message
@@ -102,7 +102,7 @@ module.exports = {
102
102
  reject(err);
103
103
  });
104
104
 
105
- currentServer.listen(port, host, () => resolve({host,port, dockerStatus: containerManager._alive}));
105
+ currentServer.listen(port, host, () => resolve({host,port, dockerStatus: containerManager.isAlive()}));
106
106
  currentServer.host = host;
107
107
  currentServer.port = port;
108
108
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.0.76",
3
+ "version": "0.1.0",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -23,9 +23,8 @@
23
23
  "dependencies": {
24
24
  "@kapeta/codegen": "<2",
25
25
  "@kapeta/local-cluster-config": "<2",
26
- "@kapeta/local-cluster-executor": "<2",
27
26
  "@kapeta/nodejs-api-client": "<2",
28
- "@kapeta/nodejs-registry-utils": "^0.0.2",
27
+ "@kapeta/nodejs-registry-utils": "<2",
29
28
  "@kapeta/nodejs-utils": "<2",
30
29
  "@kapeta/sdk-config": "<2",
31
30
  "express": "4.17.1",
@@ -54,7 +54,10 @@ class AssetManager {
54
54
  */
55
55
  getAssets(assetKinds) {
56
56
  if (!assetKinds) {
57
- const blockTypeProviders = ClusterConfiguration.getDefinitions('core/block-type');
57
+ const blockTypeProviders = ClusterConfiguration.getDefinitions([
58
+ 'core/block-type',
59
+ 'core/block-type-operator'
60
+ ]);
58
61
  assetKinds = blockTypeProviders.map(p => {
59
62
  return `${p.definition.metadata.name}:${p.version}`
60
63
  });
@@ -82,7 +85,6 @@ class AssetManager {
82
85
 
83
86
  async getAsset(ref) {
84
87
  const uri = parseKapetaUri(ref);
85
-
86
88
  await repositoryManager.ensureAsset(uri.handle, uri.name, uri.version);
87
89
 
88
90
  let asset = ClusterConfiguration.getDefinitions()
@@ -1,7 +1,7 @@
1
- const { Docker } = require("node-docker-api");
2
- const _ = require("lodash");
3
- const os = require("os");
1
+ const {Docker} = require("node-docker-api");
4
2
  const path = require("path");
3
+ const _ = require('lodash');
4
+ const FS = require("node:fs");
5
5
 
6
6
  const LABEL_PORT_PREFIX = "kapeta_port-";
7
7
 
@@ -10,301 +10,346 @@ const HEALTH_CHECK_INTERVAL = 1000;
10
10
  const HEALTH_CHECK_MAX = 20;
11
11
 
12
12
  const promisifyStream = (stream) =>
13
- new Promise((resolve, reject) => {
14
- stream.on("data", (d) => console.log(d.toString()));
15
- stream.on("end", resolve);
16
- stream.on("error", reject);
17
- });
13
+ new Promise((resolve, reject) => {
14
+ stream.on("data", (d) => console.log(d.toString()));
15
+ stream.on("end", resolve);
16
+ stream.on("error", reject);
17
+ });
18
18
 
19
19
  class ContainerManager {
20
- constructor() {
21
- this._docker = new Docker();
22
- this._alive = false;
23
- }
24
-
25
- async initialize() {
26
- // try
27
- const connectOptions = [
28
- // use defaults: DOCKER_HOST etc from env, if available
29
- undefined,
30
- // default linux
31
- { socketPath: "/var/run/docker.sock" },
32
- // default macOS
33
- { socketPath: path.join(os.homedir(), ".docker/run/docker.sock") },
34
- // Default http
35
- { protocol: "http", host: "localhost", port: 2375 },
36
- { protocol: "https", host: "localhost", port: 2376 },
37
- ];
38
- for (const opts of connectOptions) {
39
- try {
40
- const client = new Docker(opts);
41
- await client.ping();
42
- this._docker = client;
43
- return;
44
- } catch (err) {
45
- // silently ignore bad configs
46
- }
20
+ constructor() {
21
+ this._docker = null;
22
+ this._alive = false;
47
23
  }
48
- throw new Error("Unable to connect to docker");
49
- }
50
-
51
- async ping() {
52
- await this._docker.ping();
53
- this._alive = true;
54
- }
55
-
56
- async pull(image) {
57
- let [imageName, tag] = image.split(/:/);
58
- if (!tag) {
59
- tag = "latest";
24
+
25
+ isAlive() {
26
+ return this._alive;
60
27
  }
61
28
 
62
- await this._docker.image
63
- .create(
64
- {},
65
- {
66
- fromImage: imageName,
67
- tag: tag,
29
+ async initialize() {
30
+ // try
31
+ const connectOptions = [
32
+ // use defaults: DOCKER_HOST etc from env, if available
33
+ undefined,
34
+ // default linux
35
+ {socketPath: "/var/run/docker.sock"},
36
+ // default macOS
37
+ {socketPath: path.join(os.homedir(), ".docker/run/docker.sock")},
38
+ // Default http
39
+ {protocol: "http", host: "localhost", port: 2375},
40
+ {protocol: "https", host: "localhost", port: 2376},
41
+ ];
42
+ for (const opts of connectOptions) {
43
+ try {
44
+ const client = new Docker(opts);
45
+ await client.ping();
46
+ this._docker = client;
47
+ return;
48
+ } catch (err) {
49
+ // silently ignore bad configs
50
+ }
68
51
  }
69
- )
70
- .then((stream) => promisifyStream(stream));
71
- }
72
-
73
- /**
74
- *
75
- * @param {string} image
76
- * @param {string} name
77
- * @param {{ports:{},mounts:{},env:{}}} opts
78
- * @return {Promise<ContainerInfo>}
79
- */
80
- async run(image, name, opts) {
81
- const Mounts = [];
82
- const PortBindings = {};
83
- const Env = [];
84
- const Labels = {
85
- kapeta: "true",
86
- };
87
-
88
- console.log("Pulling image: %s", image);
89
-
90
- await this.pull(image);
91
-
92
- console.log("Image pulled: %s", image);
93
-
94
- _.forEach(opts.ports, (portInfo, containerPort) => {
95
- PortBindings["" + containerPort] = [
96
- {
97
- HostPort: "" + portInfo.hostPort,
98
- },
99
- ];
100
-
101
- Labels[LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
102
- });
52
+ throw new Error("Unable to connect to docker");
53
+ }
103
54
 
104
- _.forEach(opts.mounts, (Source, Target) => {
105
- Mounts.push({
106
- Target,
107
- Source,
108
- Type: "bind",
109
- ReadOnly: false,
110
- Consistency: "consistent",
111
- });
112
- });
55
+ async ping() {
56
+ await this._docker.ping();
57
+ this._alive = true;
58
+ }
113
59
 
114
- _.forEach(opts.env, (value, name) => {
115
- Env.push(name + "=" + value);
116
- });
60
+ async ping() {
117
61
 
118
- let HealthCheck = undefined;
119
-
120
- if (opts.health) {
121
- HealthCheck = {
122
- Test: ["CMD-SHELL", opts.health.cmd],
123
- Interval: opts.health.interval
124
- ? opts.health.interval * NANO_SECOND
125
- : 5000 * NANO_SECOND,
126
- Timeout: opts.health.timeout
127
- ? opts.health.timeout * NANO_SECOND
128
- : 15000 * NANO_SECOND,
129
- Retries: opts.health.retries || 10,
130
- };
131
-
132
- console.log("Adding health check", HealthCheck);
62
+ try {
63
+ const pingResult = await this._docker.ping();
64
+ if (pingResult !== 'OK') {
65
+ throw new Error(`Ping failed: ${pingResult}`);
66
+ }
67
+ } catch (e) {
68
+ throw new Error(`Docker not running. Please start the docker daemon before running this command. Error: ${e.message}`);
69
+ }
70
+
71
+ this._alive = true;
133
72
  }
134
73
 
135
- const dockerContainer = await this._docker.container.create({
136
- name: name,
137
- Image: image,
138
- Labels,
139
- Env,
140
- HealthCheck,
141
- HostConfig: {
142
- PortBindings,
143
- Mounts,
144
- },
145
- });
74
+ async ensureAlive() {
75
+ if (!this._alive) {
76
+ await this.ping();
77
+ }
78
+ }
146
79
 
147
- await dockerContainer.start();
80
+ async docker() {
81
+ await this.ensureAlive();
82
+ return this._docker;
83
+ }
148
84
 
149
- if (opts.health) {
150
- await this._waitForHealthy(dockerContainer);
85
+ async getContainerByName(containerName) {
86
+ const containers = await this._docker.container.list({all: true});
87
+ return containers.find(container => {
88
+ return container.data.Names.indexOf(`/${containerName}`) > -1;
89
+ });
151
90
  }
152
91
 
153
- return new ContainerInfo(dockerContainer);
154
- }
92
+ async pull(image) {
93
+ let [imageName, tag] = image.split(/:/);
94
+ if (!tag) {
95
+ tag = 'latest';
96
+ }
155
97
 
156
- async _waitForHealthy(container, attempt) {
157
- if (!attempt) {
158
- attempt = 0;
98
+ await this._docker.image
99
+ .create(
100
+ {},
101
+ {
102
+ fromImage: imageName,
103
+ tag: tag,
104
+ }
105
+ )
106
+ .then((stream) => promisifyStream(stream));
159
107
  }
160
108
 
161
- if (attempt >= HEALTH_CHECK_MAX) {
162
- throw new Error("Operator did not become healthy within the timeout");
109
+ /**
110
+ *
111
+ * @param {string} image
112
+ * @param {string} name
113
+ * @param {{ports:{},mounts:{},env:{}}} opts
114
+ * @return {Promise<ContainerInfo>}
115
+ */
116
+ async run(image, name, opts) {
117
+ const Mounts = [];
118
+ const PortBindings = {};
119
+ const Env = [];
120
+ const Labels = {
121
+ kapeta: "true",
122
+ };
123
+
124
+ console.log("Pulling image: %s", image);
125
+
126
+ await this.pull(image);
127
+
128
+ console.log("Image pulled: %s", image);
129
+
130
+ _.forEach(opts.ports, (portInfo, containerPort) => {
131
+ PortBindings['' + containerPort] = [
132
+ {
133
+ HostPort: '' + portInfo.hostPort,
134
+ HostIp: '127.0.0.1'
135
+ }
136
+ ];
137
+
138
+ Labels[LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
139
+ });
140
+
141
+ _.forEach(opts.mounts, (Source, Target) => {
142
+ Mounts.push({
143
+ Target,
144
+ Source,
145
+ Type: "bind",
146
+ ReadOnly: false,
147
+ Consistency: "consistent",
148
+ });
149
+ });
150
+
151
+ _.forEach(opts.env, (value, name) => {
152
+ Env.push(name + "=" + value);
153
+ });
154
+
155
+ let HealthCheck = undefined;
156
+
157
+ if (opts.health) {
158
+ HealthCheck = {
159
+ Test: ["CMD-SHELL", opts.health.cmd],
160
+ Interval: opts.health.interval
161
+ ? opts.health.interval * NANO_SECOND
162
+ : 5000 * NANO_SECOND,
163
+ Timeout: opts.health.timeout
164
+ ? opts.health.timeout * NANO_SECOND
165
+ : 15000 * NANO_SECOND,
166
+ Retries: opts.health.retries || 10,
167
+ };
168
+
169
+ console.log("Adding health check", HealthCheck);
170
+ }
171
+
172
+
173
+ const dockerContainer = await this.startContainer({
174
+ name: name,
175
+ Image: image,
176
+ Labels,
177
+ Env,
178
+ HealthCheck,
179
+ HostConfig: {
180
+ PortBindings,
181
+ Mounts
182
+ }
183
+ });
184
+
185
+ if (opts.health) {
186
+ await this._waitForHealthy(dockerContainer);
187
+ }
188
+
189
+ return new ContainerInfo(dockerContainer);
163
190
  }
164
191
 
165
- if (await this._isHealthy(container)) {
166
- console.log("Container became healthy");
167
- return;
192
+ async startContainer(opts) {
193
+ const dockerContainer = await this._docker.container.create(opts);
194
+
195
+ await dockerContainer.start();
196
+
197
+ return dockerContainer;
168
198
  }
169
199
 
170
- return new Promise((resolve) => {
171
- setTimeout(async () => {
172
- await this._waitForHealthy(container, attempt + 1);
173
- resolve();
174
- }, HEALTH_CHECK_INTERVAL);
175
- });
176
- }
177
-
178
- async _isHealthy(container) {
179
- const info = await container.status();
180
-
181
- return info?.data?.State?.Health?.Status === "healthy";
182
- }
183
-
184
- /**
185
- *
186
- * @param name
187
- * @return {Promise<ContainerInfo>}
188
- */
189
- async get(name) {
190
- let dockerContainer = null;
191
-
192
- try {
193
- dockerContainer = await this._docker.container.get(name);
194
- await dockerContainer.status();
195
- } catch (err) {
196
- //Ignore
197
- console.log("Container not available - creating it: %s", name);
198
- dockerContainer = null;
200
+
201
+ async _waitForHealthy(container, attempt) {
202
+ if (!attempt) {
203
+ attempt = 0;
204
+ }
205
+
206
+ if (attempt >= HEALTH_CHECK_MAX) {
207
+ throw new Error("Operator did not become healthy within the timeout");
208
+ }
209
+
210
+ if (await this._isHealthy(container)) {
211
+ console.log("Container became healthy");
212
+ return;
213
+ }
214
+
215
+ return new Promise((resolve) => {
216
+ setTimeout(async () => {
217
+ await this._waitForHealthy(container, attempt + 1);
218
+ resolve();
219
+ }, HEALTH_CHECK_INTERVAL);
220
+ });
199
221
  }
200
222
 
201
- if (!dockerContainer) {
202
- return null;
223
+ async _isHealthy(container) {
224
+ const info = await container.status();
225
+
226
+ return info?.data?.State?.Health?.Status === "healthy";
203
227
  }
204
228
 
205
- return new ContainerInfo(dockerContainer);
206
- }
229
+ /**
230
+ *
231
+ * @param name
232
+ * @return {Promise<ContainerInfo>}
233
+ */
234
+ async get(name) {
235
+ let dockerContainer = null;
236
+
237
+ try {
238
+ dockerContainer = await this._docker.container.get(name);
239
+ await dockerContainer.status();
240
+ } catch (err) {
241
+ //Ignore
242
+ console.log("Container not available - creating it: %s", name);
243
+ dockerContainer = null;
244
+ }
245
+
246
+ if (!dockerContainer) {
247
+ return null;
248
+ }
249
+
250
+ return new ContainerInfo(dockerContainer);
251
+ }
207
252
  }
208
253
 
209
254
  class ContainerInfo {
210
- /**
211
- *
212
- * @param {Container} dockerContainer
213
- */
214
- constructor(dockerContainer) {
215
255
  /**
216
256
  *
217
- * @type {Container}
218
- * @private
257
+ * @param {Container} dockerContainer
219
258
  */
220
- this._container = dockerContainer;
221
- }
222
-
223
- async isRunning() {
224
- const inspectResult = await this.getStatus();
225
-
226
- if (!inspectResult || !inspectResult.State) {
227
- return false;
259
+ constructor(dockerContainer) {
260
+ /**
261
+ *
262
+ * @type {Container}
263
+ * @private
264
+ */
265
+ this._container = dockerContainer;
228
266
  }
229
267
 
230
- return inspectResult.State.Running || inspectResult.State.Restarting;
231
- }
268
+ async isRunning() {
269
+ const inspectResult = await this.getStatus();
232
270
 
233
- async start() {
234
- await this._container.start();
235
- }
271
+ if (!inspectResult || !inspectResult.State) {
272
+ return false;
273
+ }
236
274
 
237
- async restart() {
238
- await this._container.restart();
239
- }
275
+ return inspectResult.State.Running || inspectResult.State.Restarting;
276
+ }
240
277
 
241
- async stop() {
242
- await this._container.stop();
243
- }
278
+ async start() {
279
+ await this._container.start();
280
+ }
244
281
 
245
- async remove(opts) {
246
- await this._container.delete({ force: !!opts.force });
247
- }
282
+ async restart() {
283
+ await this._container.restart();
284
+ }
248
285
 
249
- async getPort(type) {
250
- const ports = await this.getPorts();
286
+ async stop() {
287
+ await this._container.stop();
288
+ }
251
289
 
252
- if (ports[type]) {
253
- return ports[type];
290
+ async remove(opts) {
291
+ await this._container.delete({force: !!opts.force});
254
292
  }
255
293
 
256
- return null;
257
- }
294
+ async getPort(type) {
295
+ const ports = await this.getPorts();
258
296
 
259
- async getStatus() {
260
- const result = await this._container.status();
297
+ if (ports[type]) {
298
+ return ports[type];
299
+ }
261
300
 
262
- return result ? result.data : null;
263
- }
301
+ return null;
302
+ }
264
303
 
265
- async getPorts() {
266
- const inspectResult = await this.getStatus();
304
+ async getStatus() {
305
+ const result = await this._container.status();
267
306
 
268
- if (
269
- !inspectResult ||
270
- !inspectResult.Config ||
271
- !inspectResult.Config.Labels
272
- ) {
273
- return false;
307
+ return result ? result.data : null;
274
308
  }
275
309
 
276
- const portTypes = {};
277
- const ports = {};
310
+ async getPorts() {
311
+ const inspectResult = await this.getStatus();
278
312
 
279
- _.forEach(inspectResult.Config.Labels, (portType, name) => {
280
- if (!name.startsWith(LABEL_PORT_PREFIX)) {
281
- return;
282
- }
313
+ if (
314
+ !inspectResult ||
315
+ !inspectResult.Config ||
316
+ !inspectResult.Config.Labels
317
+ ) {
318
+ return false;
319
+ }
283
320
 
284
- const hostPort = name.substr(LABEL_PORT_PREFIX.length);
321
+ const portTypes = {};
322
+ const ports = {};
285
323
 
286
- portTypes[hostPort] = portType;
287
- });
324
+ _.forEach(inspectResult.Config.Labels, (portType, name) => {
325
+ if (!name.startsWith(LABEL_PORT_PREFIX)) {
326
+ return;
327
+ }
288
328
 
289
- _.forEach(
290
- inspectResult.HostConfig.PortBindings,
291
- (portBindings, containerPortSpec) => {
292
- let [containerPort, protocol] = containerPortSpec.split(/\//);
329
+ const hostPort = name.substr(LABEL_PORT_PREFIX.length);
293
330
 
294
- const hostPort = portBindings[0].HostPort;
331
+ portTypes[hostPort] = portType;
332
+ });
295
333
 
296
- const portType = portTypes[hostPort];
334
+ _.forEach(
335
+ inspectResult.HostConfig.PortBindings,
336
+ (portBindings, containerPortSpec) => {
337
+ let [containerPort, protocol] = containerPortSpec.split(/\//);
297
338
 
298
- ports[portType] = {
299
- containerPort,
300
- protocol,
301
- hostPort,
302
- };
303
- }
304
- );
339
+ const hostPort = portBindings[0].HostPort;
305
340
 
306
- return ports;
307
- }
341
+ const portType = portTypes[hostPort];
342
+
343
+ ports[portType] = {
344
+ containerPort,
345
+ protocol,
346
+ hostPort,
347
+ };
348
+ }
349
+ );
350
+
351
+ return ports;
352
+ }
308
353
  }
309
354
 
310
355
  module.exports = new ContainerManager();