@kapeta/local-cluster-service 0.6.0 → 0.7.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.
Files changed (193) hide show
  1. package/.eslintrc.cjs +17 -0
  2. package/.github/workflows/main.yml +22 -22
  3. package/.prettierignore +4 -0
  4. package/.vscode/launch.json +2 -4
  5. package/CHANGELOG.md +14 -0
  6. package/definitions.d.ts +17 -35
  7. package/dist/cjs/index.d.ts +27 -0
  8. package/dist/cjs/index.js +126 -0
  9. package/dist/cjs/package.json +1 -0
  10. package/dist/cjs/src/assetManager.d.ts +31 -0
  11. package/dist/cjs/src/assetManager.js +153 -0
  12. package/dist/cjs/src/assets/routes.d.ts +3 -0
  13. package/dist/cjs/src/assets/routes.js +117 -0
  14. package/dist/cjs/src/clusterService.d.ts +40 -0
  15. package/dist/cjs/src/clusterService.js +114 -0
  16. package/dist/cjs/src/codeGeneratorManager.d.ts +8 -0
  17. package/dist/cjs/src/codeGeneratorManager.js +53 -0
  18. package/dist/cjs/src/config/routes.d.ts +3 -0
  19. package/dist/cjs/src/config/routes.js +126 -0
  20. package/dist/cjs/src/configManager.d.ts +36 -0
  21. package/dist/cjs/src/configManager.js +110 -0
  22. package/dist/cjs/src/containerManager.d.ts +89 -0
  23. package/dist/cjs/src/containerManager.js +365 -0
  24. package/dist/cjs/src/filesystem/routes.d.ts +3 -0
  25. package/dist/cjs/src/filesystem/routes.js +69 -0
  26. package/dist/cjs/src/filesystemManager.d.ts +15 -0
  27. package/dist/cjs/src/filesystemManager.js +87 -0
  28. package/dist/cjs/src/identities/routes.d.ts +3 -0
  29. package/dist/cjs/src/identities/routes.js +18 -0
  30. package/dist/cjs/src/instanceManager.d.ts +56 -0
  31. package/dist/cjs/src/instanceManager.js +424 -0
  32. package/dist/cjs/src/instances/routes.d.ts +3 -0
  33. package/dist/cjs/src/instances/routes.js +134 -0
  34. package/dist/cjs/src/middleware/cors.d.ts +2 -0
  35. package/dist/cjs/src/middleware/cors.js +10 -0
  36. package/dist/cjs/src/middleware/kapeta.d.ts +11 -0
  37. package/dist/cjs/src/middleware/kapeta.js +17 -0
  38. package/dist/cjs/src/middleware/stringBody.d.ts +5 -0
  39. package/dist/cjs/src/middleware/stringBody.js +14 -0
  40. package/dist/cjs/src/networkManager.d.ts +32 -0
  41. package/dist/cjs/src/networkManager.js +109 -0
  42. package/dist/cjs/src/operatorManager.d.ts +36 -0
  43. package/dist/cjs/src/operatorManager.js +165 -0
  44. package/dist/cjs/src/progressListener.d.ts +20 -0
  45. package/dist/cjs/src/progressListener.js +91 -0
  46. package/dist/cjs/src/providerManager.d.ts +9 -0
  47. package/dist/cjs/src/providerManager.js +51 -0
  48. package/dist/cjs/src/providers/routes.d.ts +3 -0
  49. package/dist/cjs/src/providers/routes.js +42 -0
  50. package/dist/cjs/src/proxy/routes.d.ts +3 -0
  51. package/dist/cjs/src/proxy/routes.js +111 -0
  52. package/dist/cjs/src/proxy/types/rest.d.ts +4 -0
  53. package/dist/cjs/src/proxy/types/rest.js +114 -0
  54. package/dist/cjs/src/proxy/types/web.d.ts +4 -0
  55. package/dist/cjs/src/proxy/types/web.js +53 -0
  56. package/dist/cjs/src/repositoryManager.d.ts +17 -0
  57. package/dist/cjs/src/repositoryManager.js +215 -0
  58. package/dist/cjs/src/serviceManager.d.ts +29 -0
  59. package/dist/cjs/src/serviceManager.js +99 -0
  60. package/dist/cjs/src/socketManager.d.ts +14 -0
  61. package/dist/cjs/src/socketManager.js +53 -0
  62. package/dist/cjs/src/storageService.d.ts +17 -0
  63. package/dist/cjs/src/storageService.js +74 -0
  64. package/dist/cjs/src/traffic/routes.d.ts +3 -0
  65. package/dist/cjs/src/traffic/routes.js +18 -0
  66. package/dist/cjs/src/types.d.ts +88 -0
  67. package/dist/cjs/src/types.js +2 -0
  68. package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +29 -0
  69. package/dist/cjs/src/utils/BlockInstanceRunner.js +468 -0
  70. package/dist/cjs/src/utils/LogData.d.ts +19 -0
  71. package/dist/cjs/src/utils/LogData.js +43 -0
  72. package/dist/cjs/src/utils/pathTemplateParser.d.ts +26 -0
  73. package/dist/cjs/src/utils/pathTemplateParser.js +121 -0
  74. package/dist/cjs/src/utils/utils.d.ts +1 -0
  75. package/dist/cjs/src/utils/utils.js +18 -0
  76. package/dist/cjs/start.d.ts +1 -0
  77. package/dist/cjs/start.js +12 -0
  78. package/dist/esm/index.d.ts +27 -0
  79. package/dist/esm/index.js +121 -0
  80. package/dist/esm/package.json +1 -0
  81. package/dist/esm/src/assetManager.d.ts +31 -0
  82. package/{src → dist/esm/src}/assetManager.js +22 -60
  83. package/dist/esm/src/assets/routes.d.ts +3 -0
  84. package/{src → dist/esm/src}/assets/routes.js +21 -36
  85. package/dist/esm/src/clusterService.d.ts +40 -0
  86. package/{src → dist/esm/src}/clusterService.js +14 -37
  87. package/dist/esm/src/codeGeneratorManager.d.ts +8 -0
  88. package/{src → dist/esm/src}/codeGeneratorManager.js +15 -24
  89. package/dist/esm/src/config/routes.d.ts +3 -0
  90. package/dist/esm/src/config/routes.js +121 -0
  91. package/dist/esm/src/configManager.d.ts +36 -0
  92. package/{src → dist/esm/src}/configManager.js +11 -40
  93. package/dist/esm/src/containerManager.d.ts +89 -0
  94. package/{src → dist/esm/src}/containerManager.js +81 -182
  95. package/dist/esm/src/filesystem/routes.d.ts +3 -0
  96. package/dist/esm/src/filesystem/routes.js +64 -0
  97. package/dist/esm/src/filesystemManager.d.ts +15 -0
  98. package/{src → dist/esm/src}/filesystemManager.js +20 -28
  99. package/dist/esm/src/identities/routes.d.ts +3 -0
  100. package/dist/esm/src/identities/routes.js +13 -0
  101. package/dist/esm/src/instanceManager.d.ts +56 -0
  102. package/{src → dist/esm/src}/instanceManager.js +94 -175
  103. package/dist/esm/src/instances/routes.d.ts +3 -0
  104. package/{src → dist/esm/src}/instances/routes.js +31 -70
  105. package/dist/esm/src/middleware/cors.d.ts +2 -0
  106. package/{src → dist/esm/src}/middleware/cors.js +2 -3
  107. package/dist/esm/src/middleware/kapeta.d.ts +11 -0
  108. package/{src → dist/esm/src}/middleware/kapeta.js +3 -7
  109. package/dist/esm/src/middleware/stringBody.d.ts +5 -0
  110. package/{src → dist/esm/src}/middleware/stringBody.js +2 -3
  111. package/dist/esm/src/networkManager.d.ts +32 -0
  112. package/{src → dist/esm/src}/networkManager.js +16 -33
  113. package/dist/esm/src/operatorManager.d.ts +36 -0
  114. package/{src → dist/esm/src}/operatorManager.js +35 -91
  115. package/dist/esm/src/progressListener.d.ts +20 -0
  116. package/dist/esm/src/progressListener.js +88 -0
  117. package/dist/esm/src/providerManager.d.ts +9 -0
  118. package/dist/esm/src/providerManager.js +45 -0
  119. package/dist/esm/src/providers/routes.d.ts +3 -0
  120. package/{src → dist/esm/src}/providers/routes.js +10 -16
  121. package/dist/esm/src/proxy/routes.d.ts +3 -0
  122. package/dist/esm/src/proxy/routes.js +106 -0
  123. package/dist/esm/src/proxy/types/rest.d.ts +4 -0
  124. package/dist/esm/src/proxy/types/rest.js +107 -0
  125. package/dist/esm/src/proxy/types/web.d.ts +4 -0
  126. package/{src → dist/esm/src}/proxy/types/web.js +13 -35
  127. package/dist/esm/src/repositoryManager.d.ts +17 -0
  128. package/dist/esm/src/repositoryManager.js +209 -0
  129. package/dist/esm/src/serviceManager.d.ts +29 -0
  130. package/{src → dist/esm/src}/serviceManager.js +12 -42
  131. package/dist/esm/src/socketManager.d.ts +14 -0
  132. package/{src → dist/esm/src}/socketManager.js +19 -23
  133. package/dist/esm/src/storageService.d.ts +17 -0
  134. package/{src → dist/esm/src}/storageService.js +8 -27
  135. package/dist/esm/src/traffic/routes.d.ts +3 -0
  136. package/{src → dist/esm/src}/traffic/routes.js +4 -9
  137. package/dist/esm/src/types.d.ts +88 -0
  138. package/dist/esm/src/types.js +1 -0
  139. package/dist/esm/src/utils/BlockInstanceRunner.d.ts +29 -0
  140. package/{src → dist/esm/src}/utils/BlockInstanceRunner.js +137 -256
  141. package/dist/esm/src/utils/LogData.d.ts +19 -0
  142. package/{src → dist/esm/src}/utils/LogData.js +11 -22
  143. package/dist/esm/src/utils/pathTemplateParser.d.ts +26 -0
  144. package/{src → dist/esm/src}/utils/pathTemplateParser.js +21 -40
  145. package/dist/esm/src/utils/utils.d.ts +1 -0
  146. package/dist/esm/src/utils/utils.js +11 -0
  147. package/dist/esm/start.d.ts +1 -0
  148. package/dist/esm/start.js +7 -0
  149. package/index.ts +147 -0
  150. package/package.json +106 -74
  151. package/src/assetManager.ts +191 -0
  152. package/src/assets/routes.ts +132 -0
  153. package/src/clusterService.ts +134 -0
  154. package/src/codeGeneratorManager.ts +57 -0
  155. package/src/config/routes.ts +159 -0
  156. package/src/configManager.ts +148 -0
  157. package/src/containerManager.ts +466 -0
  158. package/src/filesystem/routes.ts +74 -0
  159. package/src/filesystemManager.ts +93 -0
  160. package/src/identities/routes.ts +20 -0
  161. package/src/instanceManager.ts +503 -0
  162. package/src/instances/routes.ts +164 -0
  163. package/src/middleware/cors.ts +9 -0
  164. package/src/middleware/kapeta.ts +27 -0
  165. package/src/middleware/stringBody.ts +16 -0
  166. package/src/networkManager.ts +137 -0
  167. package/src/operatorManager.ts +221 -0
  168. package/src/progressListener.ts +102 -0
  169. package/src/{providerManager.js → providerManager.ts} +15 -31
  170. package/src/providers/routes.ts +46 -0
  171. package/src/proxy/routes.ts +148 -0
  172. package/src/proxy/types/{rest.js → rest.ts} +30 -30
  173. package/src/proxy/types/web.ts +60 -0
  174. package/src/{repositoryManager.js → repositoryManager.ts} +45 -73
  175. package/src/serviceManager.ts +120 -0
  176. package/src/socketManager.ts +57 -0
  177. package/src/storageService.ts +88 -0
  178. package/src/traffic/routes.ts +18 -0
  179. package/src/types.ts +97 -0
  180. package/src/utils/BlockInstanceRunner.ts +555 -0
  181. package/src/utils/LogData.ts +47 -0
  182. package/src/utils/pathTemplateParser.ts +138 -0
  183. package/src/utils/utils.ts +12 -0
  184. package/start.ts +8 -0
  185. package/tsconfig.json +13 -0
  186. package/index.js +0 -127
  187. package/src/config/routes.js +0 -160
  188. package/src/filesystem/routes.js +0 -74
  189. package/src/identities/routes.js +0 -19
  190. package/src/progressListener.js +0 -82
  191. package/src/proxy/routes.js +0 -126
  192. package/src/utils/utils.js +0 -13
  193. package/start.js +0 -7
@@ -0,0 +1,424 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.instanceManager = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const request_1 = __importDefault(require("request"));
9
+ const events_1 = __importDefault(require("events"));
10
+ const BlockInstanceRunner_1 = require("./utils/BlockInstanceRunner");
11
+ const storageService_1 = require("./storageService");
12
+ const socketManager_1 = require("./socketManager");
13
+ const serviceManager_1 = require("./serviceManager");
14
+ const assetManager_1 = require("./assetManager");
15
+ const containerManager_1 = require("./containerManager");
16
+ const configManager_1 = require("./configManager");
17
+ const CHECK_INTERVAL = 10000;
18
+ const DEFAULT_HEALTH_PORT_TYPE = 'rest';
19
+ const EVENT_STATUS_CHANGED = 'status-changed';
20
+ const EVENT_INSTANCE_CREATED = 'instance-created';
21
+ const EVENT_INSTANCE_EXITED = 'instance-exited';
22
+ const EVENT_INSTANCE_LOG = 'instance-log';
23
+ const STATUS_STARTING = 'starting';
24
+ const STATUS_READY = 'ready';
25
+ const STATUS_UNHEALTHY = 'unhealthy';
26
+ const STATUS_STOPPED = 'stopped';
27
+ const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs - it failed
28
+ class InstanceManager {
29
+ _interval;
30
+ /**
31
+ * Contains an array of running instances that have self-registered with this
32
+ * cluster service. This is done by the Kapeta SDKs
33
+ */
34
+ _instances = [];
35
+ /**
36
+ * Contains the process info for the instances started by this manager. In memory only
37
+ * so can't be relied on for knowing everything that's running.
38
+ *
39
+ */
40
+ _processes = {};
41
+ constructor() {
42
+ this._interval = setInterval(() => this._checkInstances(), CHECK_INTERVAL);
43
+ this._instances = storageService_1.storageService.section('instances', []);
44
+ this._processes = {};
45
+ this._checkInstances();
46
+ }
47
+ _save() {
48
+ storageService_1.storageService.put('instances', this._instances);
49
+ }
50
+ async _checkInstances() {
51
+ let changed = false;
52
+ for (let i = 0; i < this._instances.length; i++) {
53
+ const instance = this._instances[i];
54
+ const newStatus = await this._getInstanceStatus(instance);
55
+ if (newStatus === STATUS_UNHEALTHY && instance.status === STATUS_STARTING) {
56
+ // If instance is starting we consider unhealthy an indication
57
+ // that it is still starting
58
+ continue;
59
+ }
60
+ if (instance.status !== newStatus) {
61
+ instance.status = newStatus;
62
+ console.log('Instance status changed: %s %s -> %s', instance.systemId, instance.instanceId, instance.status);
63
+ this._emit(instance.systemId, EVENT_STATUS_CHANGED, instance);
64
+ changed = true;
65
+ }
66
+ }
67
+ if (changed) {
68
+ this._save();
69
+ }
70
+ }
71
+ async _isRunning(instance) {
72
+ if (!instance.pid) {
73
+ return;
74
+ }
75
+ if (instance.type === 'docker') {
76
+ const container = await containerManager_1.containerManager.get(instance.pid);
77
+ if (!container) {
78
+ console.warn('Container not found: %s', instance.pid);
79
+ return false;
80
+ }
81
+ return await container.isRunning();
82
+ }
83
+ //Otherwise its just a normal process.
84
+ //TODO: Handle for Windows
85
+ try {
86
+ return process.kill(instance.pid, 0);
87
+ }
88
+ catch (err) {
89
+ return err.code === 'EPERM';
90
+ }
91
+ }
92
+ async _getInstanceStatus(instance) {
93
+ if (instance.status === STATUS_STOPPED) {
94
+ //Will only change when it reregisters
95
+ return STATUS_STOPPED;
96
+ }
97
+ if (!(await this._isRunning(instance))) {
98
+ return STATUS_STOPPED;
99
+ }
100
+ if (!instance.health) {
101
+ //No health url means we assume it's healthy as soon as it's running
102
+ return STATUS_READY;
103
+ }
104
+ return new Promise((resolve) => {
105
+ if (!instance.health) {
106
+ resolve(STATUS_READY);
107
+ return;
108
+ }
109
+ (0, request_1.default)(instance.health, (err, response) => {
110
+ if (err) {
111
+ resolve(STATUS_UNHEALTHY);
112
+ return;
113
+ }
114
+ if (response.statusCode > 399) {
115
+ resolve(STATUS_UNHEALTHY);
116
+ return;
117
+ }
118
+ resolve(STATUS_READY);
119
+ });
120
+ });
121
+ }
122
+ getInstances() {
123
+ if (!this._instances) {
124
+ return [];
125
+ }
126
+ return [...this._instances];
127
+ }
128
+ getInstancesForPlan(systemId) {
129
+ if (!this._instances) {
130
+ return [];
131
+ }
132
+ return this._instances.filter((instance) => instance.systemId === systemId);
133
+ }
134
+ /**
135
+ * Get instance information
136
+ *
137
+ * @param {string} systemId
138
+ * @param {string} instanceId
139
+ * @return {*}
140
+ */
141
+ getInstance(systemId, instanceId) {
142
+ return lodash_1.default.find(this._instances, { systemId, instanceId });
143
+ }
144
+ /**
145
+ *
146
+ * @param {string} systemId
147
+ * @param {string} instanceId
148
+ * @param {InstanceInfo} info
149
+ * @return {Promise<void>}
150
+ */
151
+ async registerInstance(systemId, instanceId, info) {
152
+ let instance = this.getInstance(systemId, instanceId);
153
+ //Get target address
154
+ let address = await serviceManager_1.serviceManager.getProviderAddress(systemId, instanceId, info.portType ?? DEFAULT_HEALTH_PORT_TYPE);
155
+ let healthUrl = null;
156
+ let health = info.health;
157
+ if (health) {
158
+ if (health.startsWith('/')) {
159
+ health = health.substring(1);
160
+ }
161
+ healthUrl = address + health;
162
+ }
163
+ if (instance) {
164
+ instance.status = STATUS_STARTING;
165
+ instance.pid = info.pid;
166
+ instance.address = address;
167
+ if (info.type) {
168
+ instance.type = info.type;
169
+ }
170
+ if (healthUrl) {
171
+ instance.health = healthUrl;
172
+ }
173
+ this._emit(systemId, EVENT_STATUS_CHANGED, instance);
174
+ }
175
+ else {
176
+ instance = {
177
+ systemId,
178
+ instanceId,
179
+ status: STATUS_STARTING,
180
+ pid: info.pid,
181
+ type: info.type,
182
+ health: healthUrl,
183
+ address,
184
+ };
185
+ this._instances.push(instance);
186
+ this._emit(systemId, EVENT_INSTANCE_CREATED, instance);
187
+ }
188
+ this._save();
189
+ }
190
+ setInstanceAsStopped(systemId, instanceId) {
191
+ const instance = lodash_1.default.find(this._instances, { systemId, instanceId });
192
+ if (instance) {
193
+ instance.status = STATUS_STOPPED;
194
+ instance.pid = null;
195
+ instance.health = null;
196
+ this._emit(systemId, EVENT_STATUS_CHANGED, instance);
197
+ this._save();
198
+ }
199
+ }
200
+ _emit(systemId, type, payload) {
201
+ try {
202
+ socketManager_1.socketManager.emit(`${systemId}/instances`, type, payload);
203
+ }
204
+ catch (e) {
205
+ console.warn('Failed to emit instance event: %s', e.message);
206
+ }
207
+ }
208
+ async createProcessesForPlan(planRef) {
209
+ await this.stopAllForPlan(planRef);
210
+ const plan = await assetManager_1.assetManager.getPlan(planRef, true);
211
+ if (!plan) {
212
+ throw new Error('Plan not found: ' + planRef);
213
+ }
214
+ if (!plan.spec.blocks) {
215
+ console.warn('No blocks found in plan', planRef);
216
+ return [];
217
+ }
218
+ let promises = [];
219
+ let errors = [];
220
+ for (let blockInstance of Object.values(plan.spec.blocks)) {
221
+ try {
222
+ promises.push(this.createProcess(planRef, blockInstance.id));
223
+ }
224
+ catch (e) {
225
+ errors.push(e);
226
+ }
227
+ }
228
+ const settled = await Promise.allSettled(promises);
229
+ if (errors.length > 0) {
230
+ throw errors[0];
231
+ }
232
+ return settled.map((p) => (p.status === 'fulfilled' ? p.value : null)).filter((p) => !!p);
233
+ }
234
+ async _stopInstance(instance) {
235
+ if (!instance.pid) {
236
+ return;
237
+ }
238
+ if (instance.status === 'stopped') {
239
+ return;
240
+ }
241
+ try {
242
+ if (instance.type === 'docker') {
243
+ const container = await containerManager_1.containerManager.get(instance.pid);
244
+ if (container) {
245
+ try {
246
+ await container.stop();
247
+ }
248
+ catch (e) {
249
+ console.error('Failed to stop container', e);
250
+ }
251
+ }
252
+ return;
253
+ }
254
+ process.kill(instance.pid, 'SIGTERM');
255
+ }
256
+ catch (e) {
257
+ console.error('Failed to stop process', e);
258
+ }
259
+ }
260
+ async stopAllForPlan(planRef) {
261
+ if (this._processes[planRef]) {
262
+ const promises = [];
263
+ console.log('Stopping all processes for plan', planRef);
264
+ for (let instance of Object.values(this._processes[planRef])) {
265
+ promises.push(instance.stop());
266
+ }
267
+ await Promise.all(promises);
268
+ this._processes[planRef] = {};
269
+ }
270
+ //Also stop instances not being maintained by the cluster service
271
+ const instancesForPlan = this._instances.filter((instance) => instance.systemId === planRef);
272
+ const promises = [];
273
+ for (let instance of instancesForPlan) {
274
+ promises.push(this._stopInstance(instance));
275
+ }
276
+ await Promise.all(promises);
277
+ }
278
+ async createProcess(planRef, instanceId) {
279
+ const plan = await assetManager_1.assetManager.getPlan(planRef, true);
280
+ if (!plan) {
281
+ throw new Error('Plan not found: ' + planRef);
282
+ }
283
+ const blockInstance = plan.spec && plan.spec.blocks ? lodash_1.default.find(plan.spec.blocks, { id: instanceId }) : null;
284
+ if (!blockInstance) {
285
+ throw new Error('Block instance not found: ' + instanceId);
286
+ }
287
+ const blockRef = blockInstance.block.ref;
288
+ const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
289
+ const instanceConfig = await configManager_1.configManager.getConfigForSection(planRef, instanceId);
290
+ if (!blockAsset) {
291
+ throw new Error('Block not found: ' + blockRef);
292
+ }
293
+ if (!this._processes[planRef]) {
294
+ this._processes[planRef] = {};
295
+ }
296
+ await this.stopProcess(planRef, instanceId);
297
+ const type = blockAsset.version === 'local' ? 'local' : 'docker';
298
+ const runner = new BlockInstanceRunner_1.BlockInstanceRunner(planRef);
299
+ const startTime = Date.now();
300
+ try {
301
+ const process = await runner.start(blockRef, instanceId, instanceConfig);
302
+ //emit stdout/stderr via sockets
303
+ process.output.on('data', (data) => {
304
+ const payload = {
305
+ source: 'stdout',
306
+ level: 'INFO',
307
+ message: data.toString(),
308
+ time: Date.now(),
309
+ };
310
+ this._emit(instanceId, EVENT_INSTANCE_LOG, payload);
311
+ });
312
+ process.output.on('exit', (exitCode) => {
313
+ const timeRunning = Date.now() - startTime;
314
+ const instance = this.getInstance(planRef, instanceId);
315
+ if (instance?.status === STATUS_READY) {
316
+ //It's already been running
317
+ return;
318
+ }
319
+ if (exitCode === 143 || exitCode === 137) {
320
+ //Process got SIGTERM (143) or SIGKILL (137)
321
+ //TODO: Windows?
322
+ return;
323
+ }
324
+ if (exitCode !== 0 || timeRunning < MIN_TIME_RUNNING) {
325
+ this._emit(blockInstance.id, EVENT_INSTANCE_EXITED, {
326
+ error: 'Failed to start instance',
327
+ status: EVENT_INSTANCE_EXITED,
328
+ instanceId: blockInstance.id,
329
+ });
330
+ }
331
+ });
332
+ await this.registerInstance(planRef, instanceId, {
333
+ type: process.type,
334
+ pid: process.pid ?? -1,
335
+ health: null,
336
+ portType: process.portType,
337
+ status: STATUS_STARTING,
338
+ });
339
+ return (this._processes[planRef][instanceId] = process);
340
+ }
341
+ catch (e) {
342
+ console.warn('Failed to start instance', e);
343
+ const logs = [
344
+ {
345
+ source: 'stdout',
346
+ level: 'ERROR',
347
+ message: e.message,
348
+ time: Date.now(),
349
+ },
350
+ ];
351
+ await this.registerInstance(planRef, instanceId, {
352
+ type: 'local',
353
+ pid: null,
354
+ health: null,
355
+ portType: DEFAULT_HEALTH_PORT_TYPE,
356
+ status: STATUS_UNHEALTHY,
357
+ });
358
+ this._emit(instanceId, EVENT_INSTANCE_LOG, logs[0]);
359
+ this._emit(blockInstance.id, EVENT_INSTANCE_EXITED, {
360
+ error: `Failed to start instance: ${e.message}`,
361
+ status: EVENT_INSTANCE_EXITED,
362
+ instanceId: blockInstance.id,
363
+ });
364
+ return (this._processes[planRef][instanceId] = {
365
+ pid: -1,
366
+ type,
367
+ logs: () => logs,
368
+ stop: () => Promise.resolve(),
369
+ ref: blockRef,
370
+ id: instanceId,
371
+ name: blockInstance.name,
372
+ output: new events_1.default(),
373
+ });
374
+ }
375
+ }
376
+ /**
377
+ *
378
+ * @param {string} planRef
379
+ * @param {string} instanceId
380
+ * @return {ProcessInfo|null}
381
+ */
382
+ getProcessForInstance(planRef, instanceId) {
383
+ if (!this._processes[planRef]) {
384
+ return null;
385
+ }
386
+ return this._processes[planRef][instanceId];
387
+ }
388
+ async restartIfRunning(planRef, instanceId) {
389
+ if (!this._processes[planRef] || !this._processes[planRef][instanceId]) {
390
+ return;
391
+ }
392
+ // createProcess will stop the process first if it's running
393
+ return this.createProcess(planRef, instanceId);
394
+ }
395
+ async stopProcess(planRef, instanceId) {
396
+ if (!this._processes[planRef]) {
397
+ return;
398
+ }
399
+ if (this._processes[planRef][instanceId]) {
400
+ try {
401
+ await this._processes[planRef][instanceId].stop();
402
+ }
403
+ catch (e) {
404
+ console.error('Failed to stop process for instance: %s -> %s', planRef, instanceId, e);
405
+ }
406
+ delete this._processes[planRef][instanceId];
407
+ }
408
+ }
409
+ async stopAllProcesses() {
410
+ for (let processesForPlan of Object.values(this._processes)) {
411
+ for (let processInfo of Object.values(processesForPlan)) {
412
+ await processInfo.stop();
413
+ }
414
+ }
415
+ this._processes = {};
416
+ for (let instance of this._instances) {
417
+ await this._stopInstance(instance);
418
+ }
419
+ }
420
+ }
421
+ exports.instanceManager = new InstanceManager();
422
+ process.on('exit', async () => {
423
+ await exports.instanceManager.stopAllProcesses();
424
+ });
@@ -0,0 +1,3 @@
1
+ /// <reference types="express" />
2
+ declare const router: import("express").Router;
3
+ export default router;
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const express_promise_router_1 = __importDefault(require("express-promise-router"));
7
+ const instanceManager_1 = require("../instanceManager");
8
+ const serviceManager_1 = require("../serviceManager");
9
+ const cors_1 = require("../middleware/cors");
10
+ const kapeta_1 = require("../middleware/kapeta");
11
+ const stringBody_1 = require("../middleware/stringBody");
12
+ const router = (0, express_promise_router_1.default)();
13
+ router.use('/', cors_1.corsHandler);
14
+ router.use('/', kapeta_1.kapetaHeaders);
15
+ /**
16
+ * Get all instances
17
+ */
18
+ router.get('/', (req, res) => {
19
+ res.send(instanceManager_1.instanceManager.getInstances());
20
+ });
21
+ /**
22
+ * Get all instances
23
+ */
24
+ router.get('/:systemId/instances', (req, res) => {
25
+ res.send(instanceManager_1.instanceManager.getInstancesForPlan(req.params.systemId));
26
+ });
27
+ /**
28
+ * Start all instances in a plan
29
+ */
30
+ router.post('/:systemId/start', async (req, res) => {
31
+ const processes = await instanceManager_1.instanceManager.createProcessesForPlan(req.params.systemId);
32
+ res.status(202).send({
33
+ ok: true,
34
+ processes: processes.map((p) => {
35
+ return { pid: p.pid, type: p.type };
36
+ }),
37
+ });
38
+ });
39
+ /**
40
+ * Stop all instances in plan
41
+ */
42
+ router.post('/:systemId/stop', async (req, res) => {
43
+ await instanceManager_1.instanceManager.stopAllForPlan(req.params.systemId);
44
+ res.status(202).send({
45
+ ok: true,
46
+ });
47
+ });
48
+ /**
49
+ * Start single instance in a plan
50
+ */
51
+ router.post('/:systemId/:instanceId/start', async (req, res) => {
52
+ const process = await instanceManager_1.instanceManager.createProcess(req.params.systemId, req.params.instanceId);
53
+ res.status(202).send({
54
+ ok: true,
55
+ pid: process.pid,
56
+ type: process.type,
57
+ });
58
+ });
59
+ /**
60
+ * Stop single instance in a plan
61
+ */
62
+ router.post('/:systemId/:instanceId/stop', async (req, res) => {
63
+ await instanceManager_1.instanceManager.stopProcess(req.params.systemId, req.params.instanceId);
64
+ res.status(202).send({ ok: true });
65
+ });
66
+ /**
67
+ * Get logs for instance in a plan
68
+ */
69
+ router.get('/:systemId/:instanceId/logs', (req, res) => {
70
+ const processInfo = instanceManager_1.instanceManager.getProcessForInstance(req.params.systemId, req.params.instanceId);
71
+ if (!processInfo) {
72
+ res.status(404).send({ ok: false });
73
+ return;
74
+ }
75
+ res.status(202).send({
76
+ logs: processInfo.logs(),
77
+ });
78
+ });
79
+ /**
80
+ * Get public address for instance in a plan if available
81
+ */
82
+ router.get('/:systemId/:instanceId/address/public', (req, res) => {
83
+ const instance = instanceManager_1.instanceManager.getInstance(req.params.systemId, req.params.instanceId);
84
+ if (!instance) {
85
+ res.status(404).send({ ok: false });
86
+ return;
87
+ }
88
+ if (!instance.address) {
89
+ res.status(400).send({ error: `Instance does not have an address. Make sure it's running.` });
90
+ return;
91
+ }
92
+ res.status(200).send(instance.address);
93
+ });
94
+ /**
95
+ * Get public address for particular resource on instance in a plan if available
96
+ */
97
+ router.get('/:systemId/:instanceId/provider/:portType/:resourceName/address/public', (req, res) => {
98
+ res.send(serviceManager_1.serviceManager.getConsumerAddress(req.params.systemId, req.params.instanceId, req.params.resourceName, req.params.portType, req.kapeta?.environment));
99
+ });
100
+ router.use('/', stringBody_1.stringBody);
101
+ router.use('/', (req, res, next) => {
102
+ if (!req.kapeta.blockRef) {
103
+ res.status(400).send({ error: 'Missing X-Kapeta-Block header.' });
104
+ return;
105
+ }
106
+ next();
107
+ });
108
+ /**
109
+ * Updates the full configuration for a given service.
110
+ */
111
+ router.put('/', async (req, res) => {
112
+ let instance = req.stringBody ? JSON.parse(req.stringBody) : null;
113
+ if (req.kapeta.environment === 'docker') {
114
+ //A bit hacky but we want to avoid overwriting the docker PID with a process PID
115
+ const oldInstance = instanceManager_1.instanceManager.getInstance(req.kapeta.systemId, req.kapeta.instanceId);
116
+ if (oldInstance) {
117
+ instance.pid = oldInstance.pid;
118
+ }
119
+ instance.type = 'docker';
120
+ }
121
+ else if (req.kapeta.environment === 'process') {
122
+ instance.type = 'process';
123
+ }
124
+ await instanceManager_1.instanceManager.registerInstance(req.kapeta.systemId, req.kapeta.instanceId, instance);
125
+ res.status(202).send({ ok: true });
126
+ });
127
+ /**
128
+ * Delete instance
129
+ */
130
+ router.delete('/', async (req, res) => {
131
+ await instanceManager_1.instanceManager.setInstanceAsStopped(req.kapeta.systemId, req.kapeta.instanceId);
132
+ res.status(202).send({ ok: true });
133
+ });
134
+ exports.default = router;
@@ -0,0 +1,2 @@
1
+ import { NextFunction, Request, Response } from 'express';
2
+ export declare function corsHandler(req: Request, res: Response, next: NextFunction): void;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.corsHandler = void 0;
4
+ function corsHandler(req, res, next) {
5
+ res.set('Access-Control-Allow-Origin', req.headers.origin);
6
+ res.set('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, HEAD, PATCH');
7
+ res.set('Access-Control-Allow-Headers', '*');
8
+ next();
9
+ }
10
+ exports.corsHandler = corsHandler;
@@ -0,0 +1,11 @@
1
+ import { NextFunction, Request, Response } from 'express';
2
+ import { EnvironmentType } from '../types';
3
+ export interface KapetaRequest extends Request {
4
+ kapeta?: {
5
+ blockRef: string;
6
+ instanceId: string;
7
+ systemId: string;
8
+ environment?: EnvironmentType;
9
+ };
10
+ }
11
+ export declare function kapetaHeaders(req: KapetaRequest, res: Response, next: NextFunction): void;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.kapetaHeaders = void 0;
4
+ function kapetaHeaders(req, res, next) {
5
+ let blockRef = req.headers['x-kapeta-block'];
6
+ let systemId = req.headers['x-kapeta-system'];
7
+ let instanceId = req.headers['x-kapeta-instance'];
8
+ let environment = req.headers['x-kapeta-environment'];
9
+ req.kapeta = {
10
+ blockRef,
11
+ instanceId,
12
+ systemId,
13
+ environment: environment ? environment : undefined,
14
+ };
15
+ next();
16
+ }
17
+ exports.kapetaHeaders = kapetaHeaders;
@@ -0,0 +1,5 @@
1
+ import { NextFunction, Request, Response } from 'express';
2
+ export type StringBodyRequest = Request<any> & {
3
+ stringBody?: string;
4
+ };
5
+ export declare function stringBody(req: StringBodyRequest, res: Response, next: NextFunction): void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stringBody = void 0;
4
+ function stringBody(req, res, next) {
5
+ // push the data to body
6
+ const body = [];
7
+ req.on('data', (chunk) => {
8
+ body.push(chunk);
9
+ }).on('end', () => {
10
+ req.stringBody = Buffer.concat(body).toString();
11
+ next();
12
+ });
13
+ }
14
+ exports.stringBody = stringBody;
@@ -0,0 +1,32 @@
1
+ import { Connection, SimpleRequest, SimpleResponse } from './types';
2
+ declare class NetworkManager {
3
+ private _connections;
4
+ private _sources;
5
+ private _targets;
6
+ static toConnectionId(connection: Connection): string;
7
+ constructor();
8
+ _ensureSystem(systemId: string): void;
9
+ _ensureConnection(systemId: string, connectionId: string): Traffic[];
10
+ _ensureSource(systemId: string, sourceBlockInstanceId: string): Traffic[];
11
+ _ensureTarget(systemId: string, targetBlockInstanceId: string): Traffic[];
12
+ addRequest(systemId: string, connection: Connection, request: SimpleRequest, consumerMethodId?: string, providerMethodId?: string): Traffic;
13
+ getTrafficForConnection(systemId: string, connectionId: string): Traffic[];
14
+ getTrafficForSource(systemId: string, blockInstanceId: string): Traffic[];
15
+ getTrafficForTarget(systemId: string, blockInstanceId: string): Traffic[];
16
+ }
17
+ declare class Traffic {
18
+ readonly id: string;
19
+ readonly connectionId: string;
20
+ readonly consumerMethodId: string | undefined;
21
+ readonly providerMethodId: string | undefined;
22
+ readonly created: number;
23
+ readonly request: SimpleRequest;
24
+ ended: null | number;
25
+ error: null | string;
26
+ response: SimpleResponse | null;
27
+ constructor(connection: Connection, request: SimpleRequest, consumerMethodId?: string, providerMethodId?: string);
28
+ asError(err: any): void;
29
+ withResponse(response: SimpleResponse): void;
30
+ }
31
+ export declare const networkManager: NetworkManager;
32
+ export {};