@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 +7 -0
- package/definitions.d.ts +4 -4
- package/index.js +2 -2
- package/package.json +2 -3
- package/src/assetManager.js +4 -2
- package/src/containerManager.js +287 -242
- package/src/instanceManager.js +33 -9
- package/src/instances/routes.js +10 -0
- package/src/networkManager.js +6 -6
- package/src/proxy/routes.js +16 -14
- package/src/proxy/types/rest.js +5 -6
- package/src/proxy/types/web.js +3 -5
- package/src/repositoryManager.js +16 -5
- package/src/serviceManager.js +0 -1
- package/src/utils/BlockInstanceRunner.js +435 -0
- package/src/utils/LogData.js +50 -0
- package/src/utils/utils.js +13 -0
package/src/instanceManager.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
const _ = require('lodash');
|
2
2
|
const request = require('request');
|
3
|
-
|
4
|
-
const
|
3
|
+
const EventEmitter = require("events");
|
4
|
+
const BlockInstanceRunner = require('./utils/BlockInstanceRunner');
|
5
5
|
|
6
6
|
const storageService = require('./storageService');
|
7
7
|
const socketManager = require('./socketManager');
|
@@ -68,6 +68,10 @@ class InstanceManager {
|
|
68
68
|
|
69
69
|
if (instance.status !== newStatus) {
|
70
70
|
instance.status = newStatus;
|
71
|
+
console.log(
|
72
|
+
'Instance status changed: %s %s -> %s',
|
73
|
+
instance.systemId, instance.instanceId, instance.status
|
74
|
+
)
|
71
75
|
this._emit(instance.systemId, EVENT_STATUS_CHANGED, instance);
|
72
76
|
changed = true;
|
73
77
|
}
|
@@ -85,7 +89,11 @@ class InstanceManager {
|
|
85
89
|
|
86
90
|
if (instance.type === 'docker') {
|
87
91
|
const container = await containerManager.get(instance.pid);
|
88
|
-
|
92
|
+
if (!container) {
|
93
|
+
console.warn('Container not found: %s', instance.pid);
|
94
|
+
return false;
|
95
|
+
}
|
96
|
+
return await container.isRunning();
|
89
97
|
}
|
90
98
|
|
91
99
|
//Otherwise its just a normal process.
|
@@ -185,8 +193,12 @@ class InstanceManager {
|
|
185
193
|
if (instance) {
|
186
194
|
instance.status = STATUS_STARTING;
|
187
195
|
instance.pid = info.pid;
|
188
|
-
|
189
|
-
|
196
|
+
if (info.type) {
|
197
|
+
instance.type = info.type;
|
198
|
+
}
|
199
|
+
if (healthUrl) {
|
200
|
+
instance.health = healthUrl;
|
201
|
+
}
|
190
202
|
this._emit(systemId, EVENT_STATUS_CHANGED, instance);
|
191
203
|
} else {
|
192
204
|
instance = {
|
@@ -268,7 +280,9 @@ class InstanceManager {
|
|
268
280
|
try {
|
269
281
|
if (instance.type === 'docker') {
|
270
282
|
const container = await containerManager.get(instance.pid);
|
271
|
-
|
283
|
+
if (container) {
|
284
|
+
await container.stop();
|
285
|
+
}
|
272
286
|
return;
|
273
287
|
}
|
274
288
|
process.kill(instance.pid, 'SIGTERM');
|
@@ -383,7 +397,8 @@ class InstanceManager {
|
|
383
397
|
message: e.message,
|
384
398
|
time: Date.now()
|
385
399
|
}
|
386
|
-
]
|
400
|
+
];
|
401
|
+
|
387
402
|
await this.registerInstance(planRef, instanceId, {
|
388
403
|
type: 'local',
|
389
404
|
pid: null,
|
@@ -401,7 +416,12 @@ class InstanceManager {
|
|
401
416
|
return this._processes[planRef][instanceId] = {
|
402
417
|
pid: -1,
|
403
418
|
type,
|
404
|
-
logs: () => logs
|
419
|
+
logs: () => logs,
|
420
|
+
stop: () => Promise.resolve(),
|
421
|
+
ref: blockRef,
|
422
|
+
id: instanceId,
|
423
|
+
name: blockInstance.name,
|
424
|
+
output: new EventEmitter()
|
405
425
|
};
|
406
426
|
}
|
407
427
|
|
@@ -427,7 +447,11 @@ class InstanceManager {
|
|
427
447
|
}
|
428
448
|
|
429
449
|
if (this._processes[planRef][instanceId]) {
|
430
|
-
|
450
|
+
try {
|
451
|
+
await this._processes[planRef][instanceId].stop();
|
452
|
+
} catch (e) {
|
453
|
+
console.error('Failed to stop process for instance: %s -> %s', planRef, instanceId, e);
|
454
|
+
}
|
431
455
|
delete this._processes[planRef][instanceId];
|
432
456
|
}
|
433
457
|
}
|
package/src/instances/routes.js
CHANGED
@@ -100,6 +100,16 @@ router.put('/', async (req, res) => {
|
|
100
100
|
|
101
101
|
let instance = JSON.parse(req.stringBody);
|
102
102
|
|
103
|
+
if (req.kapeta.environment === 'docker') {
|
104
|
+
//A bit hacky but we want to avoid overwriting the docker PID with a process PID
|
105
|
+
const oldInstance = instanceManager.getInstance(
|
106
|
+
req.kapeta.systemId,
|
107
|
+
req.kapeta.instanceId
|
108
|
+
);
|
109
|
+
if (oldInstance) {
|
110
|
+
instance.pid = oldInstance.pid;
|
111
|
+
}
|
112
|
+
}
|
103
113
|
await instanceManager.registerInstance(
|
104
114
|
req.kapeta.systemId,
|
105
115
|
req.kapeta.instanceId,
|
package/src/networkManager.js
CHANGED
@@ -3,10 +3,10 @@ class NetworkManager {
|
|
3
3
|
|
4
4
|
static toConnectionId(connection) {
|
5
5
|
return [
|
6
|
-
connection.
|
7
|
-
connection.
|
8
|
-
connection.
|
9
|
-
connection.
|
6
|
+
connection.provider.blockId,
|
7
|
+
connection.provider.resourceName,
|
8
|
+
connection.consumer.blockId,
|
9
|
+
connection.consumer.resourceName
|
10
10
|
].join('_');
|
11
11
|
}
|
12
12
|
|
@@ -65,8 +65,8 @@ class NetworkManager {
|
|
65
65
|
const traffic = new Traffic(connection, request, consumerMethodId, providerMethodId);
|
66
66
|
|
67
67
|
this._ensureConnection(systemId, traffic.connectionId).push(traffic);
|
68
|
-
this._ensureSource(systemId, connection.
|
69
|
-
this._ensureTarget(systemId, connection.
|
68
|
+
this._ensureSource(systemId, connection.provider.blockId).push(traffic);
|
69
|
+
this._ensureTarget(systemId, connection.consumer.blockId).push(traffic);
|
70
70
|
|
71
71
|
return traffic;
|
72
72
|
}
|
package/src/proxy/routes.js
CHANGED
@@ -34,11 +34,12 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
34
34
|
|
35
35
|
const plan = await assetManager.getPlan(req.params.systemId);
|
36
36
|
|
37
|
+
|
37
38
|
// We can find the connection by the consumer information alone since
|
38
39
|
// only 1 provider can be connected to a consumer resource at a time
|
39
40
|
const connection = _.find(plan.spec.connections, (connection) => {
|
40
|
-
return connection.
|
41
|
-
connection.
|
41
|
+
return connection.consumer.blockId.toLowerCase() === req.params.consumerInstanceId.toLowerCase() &&
|
42
|
+
connection.consumer.resourceName.toLowerCase() === req.params.consumerResourceName.toLowerCase();
|
42
43
|
});
|
43
44
|
|
44
45
|
if (!connection) {
|
@@ -47,7 +48,7 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
47
48
|
}
|
48
49
|
|
49
50
|
const toBlockInstance = _.find(plan.spec.blocks, (blockInstance) => {
|
50
|
-
return blockInstance.id.toLowerCase() === connection.
|
51
|
+
return blockInstance.id.toLowerCase() === connection.consumer.blockId.toLowerCase();
|
51
52
|
});
|
52
53
|
|
53
54
|
if (!toBlockInstance) {
|
@@ -57,9 +58,9 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
57
58
|
|
58
59
|
const toBlockAsset = await assetManager.getAsset(toBlockInstance.block.ref);
|
59
60
|
|
60
|
-
const
|
61
|
+
const consumerResource = getResource(toBlockAsset.data.spec.consumers, req.params.consumerResourceName);
|
61
62
|
|
62
|
-
if (!
|
63
|
+
if (!consumerResource) {
|
63
64
|
res.status(401).send({error:`Block resource not found "${req.params.consumerInstanceId}::${req.params.consumerResourceName}`});
|
64
65
|
return;
|
65
66
|
}
|
@@ -76,23 +77,23 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
76
77
|
Note that this might not match the path the destination is expecting so we need to identify the method
|
77
78
|
that is being called and identify the destination path from the connection.
|
78
79
|
*/
|
79
|
-
const consumerPath = req.originalUrl.
|
80
|
+
const consumerPath = req.originalUrl.substring(basePath.length - 1);
|
80
81
|
|
81
82
|
const fromBlockInstance = _.find(plan.spec.blocks, (blockInstance) => {
|
82
|
-
return blockInstance.id.toLowerCase() === connection.
|
83
|
+
return blockInstance.id.toLowerCase() === connection.provider.blockId.toLowerCase();
|
83
84
|
});
|
84
85
|
|
85
86
|
if (!fromBlockInstance) {
|
86
|
-
res.status(401).send({error:`Block instance not found "${connection.
|
87
|
+
res.status(401).send({error:`Block instance not found "${connection.provider.blockId}`});
|
87
88
|
return;
|
88
89
|
}
|
89
90
|
|
90
91
|
const fromBlockAsset = await assetManager.getAsset(fromBlockInstance.block.ref);
|
91
92
|
|
92
|
-
const
|
93
|
+
const providerResource = getResource(fromBlockAsset.data.spec.providers, connection.provider.resourceName);
|
93
94
|
|
94
|
-
if (!
|
95
|
-
res.status(401).send({error:`Block resource not found "${connection.
|
95
|
+
if (!providerResource) {
|
96
|
+
res.status(401).send({error:`Block resource not found "${connection.provider.blockId}::${connection.provider.resourceName}`});
|
96
97
|
return;
|
97
98
|
}
|
98
99
|
|
@@ -100,7 +101,7 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
100
101
|
//Get target address
|
101
102
|
let address = await serviceManager.getProviderAddress(
|
102
103
|
req.params.systemId,
|
103
|
-
connection.
|
104
|
+
connection.provider.blockId,
|
104
105
|
req.params.type
|
105
106
|
);
|
106
107
|
|
@@ -111,12 +112,13 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
111
112
|
typeHandler(req, res, {
|
112
113
|
consumerPath,
|
113
114
|
address,
|
114
|
-
|
115
|
-
|
115
|
+
consumerResource,
|
116
|
+
providerResource,
|
116
117
|
connection
|
117
118
|
});
|
118
119
|
|
119
120
|
} catch(err) {
|
121
|
+
console.warn("Failed to process proxy request", err);
|
120
122
|
res.status(400).send({error: err.message});
|
121
123
|
}
|
122
124
|
|
package/src/proxy/types/rest.js
CHANGED
@@ -35,7 +35,7 @@ function getRestMethodId(restResource, httpMethod, httpPath) {
|
|
35
35
|
* @return {{consumerMethod: *, providerMethod: *}}
|
36
36
|
*/
|
37
37
|
function resolveMethods(req, opts) {
|
38
|
-
const consumerMethodId = getRestMethodId(opts.
|
38
|
+
const consumerMethodId = getRestMethodId(opts.consumerResource, req.method, opts.consumerPath);
|
39
39
|
|
40
40
|
if (!consumerMethodId) {
|
41
41
|
throw new Error(
|
@@ -43,7 +43,7 @@ function resolveMethods(req, opts) {
|
|
43
43
|
);
|
44
44
|
}
|
45
45
|
|
46
|
-
const consumerMethod = _.cloneDeep(opts.
|
46
|
+
const consumerMethod = _.cloneDeep(opts.consumerResource.spec.methods[consumerMethodId]);
|
47
47
|
|
48
48
|
if (!consumerMethod) {
|
49
49
|
throw new Error(
|
@@ -61,11 +61,11 @@ function resolveMethods(req, opts) {
|
|
61
61
|
throw new Error(`Connection contained no mapping for consumer method "${consumerMethodId}`);
|
62
62
|
}
|
63
63
|
|
64
|
-
const providerMethod = _.cloneDeep(opts.
|
64
|
+
const providerMethod = _.cloneDeep(opts.providerResource.spec.methods[providerMethodId]);
|
65
65
|
|
66
66
|
if (!providerMethod) {
|
67
67
|
throw new Error(
|
68
|
-
`Provider method not found "${providerMethodId}" in resource "${opts.connection.
|
68
|
+
`Provider method not found "${providerMethodId}" in resource "${opts.connection.provider.blockId}::${opts.connection.provider.resourceName}`
|
69
69
|
);
|
70
70
|
}
|
71
71
|
|
@@ -106,8 +106,7 @@ module.exports = function proxyRestRequest(req, res, opts) {
|
|
106
106
|
delete requestHeaders['host'];
|
107
107
|
delete requestHeaders['origin'];
|
108
108
|
|
109
|
-
|
110
|
-
console.log('Route to provider: %s => %s', opts.consumerPath, opts.address + providerPath);
|
109
|
+
console.log('Proxy request to provider: %s => %s [rest]', opts.consumerPath, opts.address + providerPath);
|
111
110
|
|
112
111
|
const reqOpts = {
|
113
112
|
method: providerMethod.method || 'GET',
|
package/src/proxy/types/web.js
CHANGED
@@ -12,7 +12,7 @@ const socketManager = require('../../socketManager');
|
|
12
12
|
*/
|
13
13
|
module.exports = function proxyRestRequest(req, res, opts) {
|
14
14
|
|
15
|
-
console.log('
|
15
|
+
console.log('Proxy request to provider: %s => %s [web]', opts.consumerPath, opts.address);
|
16
16
|
|
17
17
|
const requestHeaders = _.clone(req.headers);
|
18
18
|
|
@@ -22,8 +22,8 @@ module.exports = function proxyRestRequest(req, res, opts) {
|
|
22
22
|
delete requestHeaders['host'];
|
23
23
|
delete requestHeaders['origin'];
|
24
24
|
|
25
|
-
const sourceBasePath = opts.
|
26
|
-
const targetBasePath = opts.
|
25
|
+
const sourceBasePath = opts.providerResource.spec.path;
|
26
|
+
const targetBasePath = opts.consumerResource.spec.path;
|
27
27
|
let path = opts.consumerPath;
|
28
28
|
if (opts.consumerPath.startsWith(sourceBasePath)) {
|
29
29
|
path = path.replace(sourceBasePath, targetBasePath);
|
@@ -37,8 +37,6 @@ module.exports = function proxyRestRequest(req, res, opts) {
|
|
37
37
|
body: req.stringBody
|
38
38
|
};
|
39
39
|
|
40
|
-
console.log('reqOpts', reqOpts);
|
41
|
-
|
42
40
|
const traffic = networkManager.addRequest(
|
43
41
|
req.params.systemId,
|
44
42
|
opts.connection,
|
package/src/repositoryManager.js
CHANGED
@@ -166,7 +166,8 @@ class RepositoryManager {
|
|
166
166
|
return null;
|
167
167
|
}
|
168
168
|
|
169
|
-
const
|
169
|
+
const definitions = ClusterConfiguration.getDefinitions();
|
170
|
+
const installedAsset = definitions.find(d =>
|
170
171
|
d.definition.metadata.name === fullName &&
|
171
172
|
d.version === version);
|
172
173
|
|
@@ -179,18 +180,28 @@ class RepositoryManager {
|
|
179
180
|
return;
|
180
181
|
}
|
181
182
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
183
|
+
try {
|
184
|
+
const assetVersion = await this._registryService.getVersion(fullName, version);
|
185
|
+
if (!assetVersion) {
|
186
|
+
this._cache[ref] = false;
|
187
|
+
return;
|
188
|
+
}
|
189
|
+
} catch (e) {
|
190
|
+
console.warn(`Unable to resolve asset: ${ref}`, e);
|
191
|
+
if (installedAsset) {
|
192
|
+
return;
|
193
|
+
}
|
194
|
+
throw e;
|
186
195
|
}
|
187
196
|
|
188
197
|
this._cache[ref] = true;
|
189
198
|
if (!installedAsset) {
|
199
|
+
console.log(`Auto-installing missing asset: ${ref}`);
|
190
200
|
await this._install([ref]);
|
191
201
|
} else {
|
192
202
|
//Ensure dependencies are installed
|
193
203
|
const refs = assetVersion.dependencies.map((dep) => dep.name);
|
204
|
+
console.log(`Auto-installing dependencies: ${refs.join(', ')}`);
|
194
205
|
await this._install(refs);
|
195
206
|
}
|
196
207
|
|