@kapeta/local-cluster-service 0.0.0-96f91ef

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 (274) hide show
  1. package/.eslintrc.cjs +25 -0
  2. package/.github/workflows/check-license.yml +17 -0
  3. package/.github/workflows/main.yml +26 -0
  4. package/.prettierignore +4 -0
  5. package/.vscode/launch.json +19 -0
  6. package/CHANGELOG.md +920 -0
  7. package/LICENSE +38 -0
  8. package/README.md +36 -0
  9. package/definitions.d.ts +35 -0
  10. package/dist/cjs/index.d.ts +34 -0
  11. package/dist/cjs/index.js +263 -0
  12. package/dist/cjs/package.json +1 -0
  13. package/dist/cjs/src/RepositoryWatcher.d.ts +30 -0
  14. package/dist/cjs/src/RepositoryWatcher.js +332 -0
  15. package/dist/cjs/src/ai/aiClient.d.ts +20 -0
  16. package/dist/cjs/src/ai/aiClient.js +74 -0
  17. package/dist/cjs/src/ai/routes.d.ts +7 -0
  18. package/dist/cjs/src/ai/routes.js +37 -0
  19. package/dist/cjs/src/ai/transform.d.ts +11 -0
  20. package/dist/cjs/src/ai/transform.js +239 -0
  21. package/dist/cjs/src/ai/types.d.ts +40 -0
  22. package/dist/cjs/src/ai/types.js +2 -0
  23. package/dist/cjs/src/api.d.ts +7 -0
  24. package/dist/cjs/src/api.js +29 -0
  25. package/dist/cjs/src/assetManager.d.ts +41 -0
  26. package/dist/cjs/src/assetManager.js +274 -0
  27. package/dist/cjs/src/assets/routes.d.ts +7 -0
  28. package/dist/cjs/src/assets/routes.js +165 -0
  29. package/dist/cjs/src/attachments/routes.d.ts +7 -0
  30. package/dist/cjs/src/attachments/routes.js +72 -0
  31. package/dist/cjs/src/authManager.d.ts +16 -0
  32. package/dist/cjs/src/authManager.js +64 -0
  33. package/dist/cjs/src/cacheManager.d.ts +20 -0
  34. package/dist/cjs/src/cacheManager.js +51 -0
  35. package/dist/cjs/src/clusterService.d.ts +44 -0
  36. package/dist/cjs/src/clusterService.js +120 -0
  37. package/dist/cjs/src/codeGeneratorManager.d.ts +14 -0
  38. package/dist/cjs/src/codeGeneratorManager.js +93 -0
  39. package/dist/cjs/src/config/routes.d.ts +7 -0
  40. package/dist/cjs/src/config/routes.js +160 -0
  41. package/dist/cjs/src/configManager.d.ts +42 -0
  42. package/dist/cjs/src/configManager.js +136 -0
  43. package/dist/cjs/src/containerManager.d.ts +148 -0
  44. package/dist/cjs/src/containerManager.js +958 -0
  45. package/dist/cjs/src/definitionsManager.d.ts +20 -0
  46. package/dist/cjs/src/definitionsManager.js +171 -0
  47. package/dist/cjs/src/filesystem/routes.d.ts +7 -0
  48. package/dist/cjs/src/filesystem/routes.js +105 -0
  49. package/dist/cjs/src/filesystemManager.d.ts +27 -0
  50. package/dist/cjs/src/filesystemManager.js +118 -0
  51. package/dist/cjs/src/identities/routes.d.ts +7 -0
  52. package/dist/cjs/src/identities/routes.js +37 -0
  53. package/dist/cjs/src/instanceManager.d.ts +69 -0
  54. package/dist/cjs/src/instanceManager.js +910 -0
  55. package/dist/cjs/src/instances/routes.d.ts +7 -0
  56. package/dist/cjs/src/instances/routes.js +179 -0
  57. package/dist/cjs/src/middleware/cors.d.ts +6 -0
  58. package/dist/cjs/src/middleware/cors.js +14 -0
  59. package/dist/cjs/src/middleware/kapeta.d.ts +15 -0
  60. package/dist/cjs/src/middleware/kapeta.js +28 -0
  61. package/dist/cjs/src/middleware/stringBody.d.ts +9 -0
  62. package/dist/cjs/src/middleware/stringBody.js +18 -0
  63. package/dist/cjs/src/networkManager.d.ts +37 -0
  64. package/dist/cjs/src/networkManager.js +119 -0
  65. package/dist/cjs/src/operatorManager.d.ts +41 -0
  66. package/dist/cjs/src/operatorManager.js +211 -0
  67. package/dist/cjs/src/progressListener.d.ts +31 -0
  68. package/dist/cjs/src/progressListener.js +133 -0
  69. package/dist/cjs/src/providerManager.d.ts +11 -0
  70. package/dist/cjs/src/providerManager.js +84 -0
  71. package/dist/cjs/src/providers/routes.d.ts +7 -0
  72. package/dist/cjs/src/providers/routes.js +46 -0
  73. package/dist/cjs/src/proxy/routes.d.ts +7 -0
  74. package/dist/cjs/src/proxy/routes.js +115 -0
  75. package/dist/cjs/src/proxy/types/rest.d.ts +10 -0
  76. package/dist/cjs/src/proxy/types/rest.js +123 -0
  77. package/dist/cjs/src/proxy/types/web.d.ts +8 -0
  78. package/dist/cjs/src/proxy/types/web.js +61 -0
  79. package/dist/cjs/src/repositoryManager.d.ts +35 -0
  80. package/dist/cjs/src/repositoryManager.js +247 -0
  81. package/dist/cjs/src/serviceManager.d.ts +36 -0
  82. package/dist/cjs/src/serviceManager.js +106 -0
  83. package/dist/cjs/src/socketManager.d.ts +32 -0
  84. package/dist/cjs/src/socketManager.js +125 -0
  85. package/dist/cjs/src/storageService.d.ts +21 -0
  86. package/dist/cjs/src/storageService.js +81 -0
  87. package/dist/cjs/src/taskManager.d.ts +70 -0
  88. package/dist/cjs/src/taskManager.js +181 -0
  89. package/dist/cjs/src/tasks/routes.d.ts +7 -0
  90. package/dist/cjs/src/tasks/routes.js +39 -0
  91. package/dist/cjs/src/traffic/routes.d.ts +7 -0
  92. package/dist/cjs/src/traffic/routes.js +22 -0
  93. package/dist/cjs/src/types.d.ts +99 -0
  94. package/dist/cjs/src/types.js +39 -0
  95. package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +28 -0
  96. package/dist/cjs/src/utils/BlockInstanceRunner.js +432 -0
  97. package/dist/cjs/src/utils/DefaultProviderInstaller.d.ts +15 -0
  98. package/dist/cjs/src/utils/DefaultProviderInstaller.js +136 -0
  99. package/dist/cjs/src/utils/InternalConfigProvider.d.ts +38 -0
  100. package/dist/cjs/src/utils/InternalConfigProvider.js +146 -0
  101. package/dist/cjs/src/utils/LogData.d.ts +23 -0
  102. package/dist/cjs/src/utils/LogData.js +46 -0
  103. package/dist/cjs/src/utils/commandLineUtils.d.ts +8 -0
  104. package/dist/cjs/src/utils/commandLineUtils.js +39 -0
  105. package/dist/cjs/src/utils/pathTemplateParser.d.ts +30 -0
  106. package/dist/cjs/src/utils/pathTemplateParser.js +135 -0
  107. package/dist/cjs/src/utils/utils.d.ts +40 -0
  108. package/dist/cjs/src/utils/utils.js +148 -0
  109. package/dist/cjs/start.d.ts +5 -0
  110. package/dist/cjs/start.js +17 -0
  111. package/dist/cjs/test/proxy/types/rest.test.d.ts +5 -0
  112. package/dist/cjs/test/proxy/types/rest.test.js +48 -0
  113. package/dist/cjs/test/utils/pathTemplateParser.test.d.ts +5 -0
  114. package/dist/cjs/test/utils/pathTemplateParser.test.js +27 -0
  115. package/dist/esm/index.d.ts +34 -0
  116. package/dist/esm/index.js +263 -0
  117. package/dist/esm/package.json +1 -0
  118. package/dist/esm/src/RepositoryWatcher.d.ts +30 -0
  119. package/dist/esm/src/RepositoryWatcher.js +332 -0
  120. package/dist/esm/src/ai/aiClient.d.ts +20 -0
  121. package/dist/esm/src/ai/aiClient.js +74 -0
  122. package/dist/esm/src/ai/routes.d.ts +7 -0
  123. package/dist/esm/src/ai/routes.js +37 -0
  124. package/dist/esm/src/ai/transform.d.ts +11 -0
  125. package/dist/esm/src/ai/transform.js +239 -0
  126. package/dist/esm/src/ai/types.d.ts +40 -0
  127. package/dist/esm/src/ai/types.js +2 -0
  128. package/dist/esm/src/api.d.ts +7 -0
  129. package/dist/esm/src/api.js +29 -0
  130. package/dist/esm/src/assetManager.d.ts +41 -0
  131. package/dist/esm/src/assetManager.js +274 -0
  132. package/dist/esm/src/assets/routes.d.ts +7 -0
  133. package/dist/esm/src/assets/routes.js +165 -0
  134. package/dist/esm/src/attachments/routes.d.ts +7 -0
  135. package/dist/esm/src/attachments/routes.js +72 -0
  136. package/dist/esm/src/authManager.d.ts +16 -0
  137. package/dist/esm/src/authManager.js +64 -0
  138. package/dist/esm/src/cacheManager.d.ts +20 -0
  139. package/dist/esm/src/cacheManager.js +51 -0
  140. package/dist/esm/src/clusterService.d.ts +44 -0
  141. package/dist/esm/src/clusterService.js +120 -0
  142. package/dist/esm/src/codeGeneratorManager.d.ts +14 -0
  143. package/dist/esm/src/codeGeneratorManager.js +93 -0
  144. package/dist/esm/src/config/routes.d.ts +7 -0
  145. package/dist/esm/src/config/routes.js +160 -0
  146. package/dist/esm/src/configManager.d.ts +42 -0
  147. package/dist/esm/src/configManager.js +136 -0
  148. package/dist/esm/src/containerManager.d.ts +148 -0
  149. package/dist/esm/src/containerManager.js +958 -0
  150. package/dist/esm/src/definitionsManager.d.ts +20 -0
  151. package/dist/esm/src/definitionsManager.js +171 -0
  152. package/dist/esm/src/filesystem/routes.d.ts +7 -0
  153. package/dist/esm/src/filesystem/routes.js +105 -0
  154. package/dist/esm/src/filesystemManager.d.ts +27 -0
  155. package/dist/esm/src/filesystemManager.js +118 -0
  156. package/dist/esm/src/identities/routes.d.ts +7 -0
  157. package/dist/esm/src/identities/routes.js +37 -0
  158. package/dist/esm/src/instanceManager.d.ts +69 -0
  159. package/dist/esm/src/instanceManager.js +910 -0
  160. package/dist/esm/src/instances/routes.d.ts +7 -0
  161. package/dist/esm/src/instances/routes.js +179 -0
  162. package/dist/esm/src/middleware/cors.d.ts +6 -0
  163. package/dist/esm/src/middleware/cors.js +14 -0
  164. package/dist/esm/src/middleware/kapeta.d.ts +15 -0
  165. package/dist/esm/src/middleware/kapeta.js +28 -0
  166. package/dist/esm/src/middleware/stringBody.d.ts +9 -0
  167. package/dist/esm/src/middleware/stringBody.js +18 -0
  168. package/dist/esm/src/networkManager.d.ts +37 -0
  169. package/dist/esm/src/networkManager.js +119 -0
  170. package/dist/esm/src/operatorManager.d.ts +41 -0
  171. package/dist/esm/src/operatorManager.js +211 -0
  172. package/dist/esm/src/progressListener.d.ts +31 -0
  173. package/dist/esm/src/progressListener.js +133 -0
  174. package/dist/esm/src/providerManager.d.ts +11 -0
  175. package/dist/esm/src/providerManager.js +84 -0
  176. package/dist/esm/src/providers/routes.d.ts +7 -0
  177. package/dist/esm/src/providers/routes.js +46 -0
  178. package/dist/esm/src/proxy/routes.d.ts +7 -0
  179. package/dist/esm/src/proxy/routes.js +115 -0
  180. package/dist/esm/src/proxy/types/rest.d.ts +10 -0
  181. package/dist/esm/src/proxy/types/rest.js +123 -0
  182. package/dist/esm/src/proxy/types/web.d.ts +8 -0
  183. package/dist/esm/src/proxy/types/web.js +61 -0
  184. package/dist/esm/src/repositoryManager.d.ts +35 -0
  185. package/dist/esm/src/repositoryManager.js +247 -0
  186. package/dist/esm/src/serviceManager.d.ts +36 -0
  187. package/dist/esm/src/serviceManager.js +106 -0
  188. package/dist/esm/src/socketManager.d.ts +32 -0
  189. package/dist/esm/src/socketManager.js +125 -0
  190. package/dist/esm/src/storageService.d.ts +21 -0
  191. package/dist/esm/src/storageService.js +81 -0
  192. package/dist/esm/src/taskManager.d.ts +70 -0
  193. package/dist/esm/src/taskManager.js +181 -0
  194. package/dist/esm/src/tasks/routes.d.ts +7 -0
  195. package/dist/esm/src/tasks/routes.js +39 -0
  196. package/dist/esm/src/traffic/routes.d.ts +7 -0
  197. package/dist/esm/src/traffic/routes.js +22 -0
  198. package/dist/esm/src/types.d.ts +99 -0
  199. package/dist/esm/src/types.js +39 -0
  200. package/dist/esm/src/utils/BlockInstanceRunner.d.ts +28 -0
  201. package/dist/esm/src/utils/BlockInstanceRunner.js +432 -0
  202. package/dist/esm/src/utils/DefaultProviderInstaller.d.ts +15 -0
  203. package/dist/esm/src/utils/DefaultProviderInstaller.js +136 -0
  204. package/dist/esm/src/utils/InternalConfigProvider.d.ts +38 -0
  205. package/dist/esm/src/utils/InternalConfigProvider.js +146 -0
  206. package/dist/esm/src/utils/LogData.d.ts +23 -0
  207. package/dist/esm/src/utils/LogData.js +46 -0
  208. package/dist/esm/src/utils/commandLineUtils.d.ts +8 -0
  209. package/dist/esm/src/utils/commandLineUtils.js +39 -0
  210. package/dist/esm/src/utils/pathTemplateParser.d.ts +30 -0
  211. package/dist/esm/src/utils/pathTemplateParser.js +135 -0
  212. package/dist/esm/src/utils/utils.d.ts +40 -0
  213. package/dist/esm/src/utils/utils.js +148 -0
  214. package/dist/esm/start.d.ts +5 -0
  215. package/dist/esm/start.js +17 -0
  216. package/dist/esm/test/proxy/types/rest.test.d.ts +5 -0
  217. package/dist/esm/test/proxy/types/rest.test.js +48 -0
  218. package/dist/esm/test/utils/pathTemplateParser.test.d.ts +5 -0
  219. package/dist/esm/test/utils/pathTemplateParser.test.js +27 -0
  220. package/index.ts +280 -0
  221. package/jest.config.js +8 -0
  222. package/package.json +134 -0
  223. package/src/RepositoryWatcher.ts +363 -0
  224. package/src/ai/aiClient.ts +93 -0
  225. package/src/ai/routes.ts +39 -0
  226. package/src/ai/transform.ts +275 -0
  227. package/src/ai/types.ts +45 -0
  228. package/src/api.ts +32 -0
  229. package/src/assetManager.ts +355 -0
  230. package/src/assets/routes.ts +183 -0
  231. package/src/attachments/routes.ts +79 -0
  232. package/src/authManager.ts +67 -0
  233. package/src/cacheManager.ts +59 -0
  234. package/src/clusterService.ts +142 -0
  235. package/src/codeGeneratorManager.ts +109 -0
  236. package/src/config/routes.ts +201 -0
  237. package/src/configManager.ts +180 -0
  238. package/src/containerManager.ts +1178 -0
  239. package/src/definitionsManager.ts +212 -0
  240. package/src/filesystem/routes.ts +123 -0
  241. package/src/filesystemManager.ts +133 -0
  242. package/src/identities/routes.ts +38 -0
  243. package/src/instanceManager.ts +1160 -0
  244. package/src/instances/routes.ts +203 -0
  245. package/src/middleware/cors.ts +14 -0
  246. package/src/middleware/kapeta.ts +41 -0
  247. package/src/middleware/stringBody.ts +21 -0
  248. package/src/networkManager.ts +148 -0
  249. package/src/operatorManager.ts +294 -0
  250. package/src/progressListener.ts +151 -0
  251. package/src/providerManager.ts +97 -0
  252. package/src/providers/routes.ts +51 -0
  253. package/src/proxy/routes.ts +153 -0
  254. package/src/proxy/types/rest.ts +172 -0
  255. package/src/proxy/types/web.ts +70 -0
  256. package/src/repositoryManager.ts +291 -0
  257. package/src/serviceManager.ts +133 -0
  258. package/src/socketManager.ts +138 -0
  259. package/src/storageService.ts +97 -0
  260. package/src/taskManager.ts +247 -0
  261. package/src/tasks/routes.ts +43 -0
  262. package/src/traffic/routes.ts +23 -0
  263. package/src/types.ts +112 -0
  264. package/src/utils/BlockInstanceRunner.ts +577 -0
  265. package/src/utils/DefaultProviderInstaller.ts +150 -0
  266. package/src/utils/InternalConfigProvider.ts +214 -0
  267. package/src/utils/LogData.ts +50 -0
  268. package/src/utils/commandLineUtils.ts +45 -0
  269. package/src/utils/pathTemplateParser.ts +157 -0
  270. package/src/utils/utils.ts +155 -0
  271. package/start.ts +14 -0
  272. package/test/proxy/types/rest.test.ts +54 -0
  273. package/test/utils/pathTemplateParser.test.ts +29 -0
  274. package/tsconfig.json +15 -0
@@ -0,0 +1,910 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright 2023 Kapeta Inc.
4
+ * SPDX-License-Identifier: BUSL-1.1
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.instanceManager = exports.InstanceManager = void 0;
11
+ const lodash_1 = __importDefault(require("lodash"));
12
+ const request_1 = __importDefault(require("request"));
13
+ const async_lock_1 = __importDefault(require("async-lock"));
14
+ const BlockInstanceRunner_1 = require("./utils/BlockInstanceRunner");
15
+ const storageService_1 = require("./storageService");
16
+ const socketManager_1 = require("./socketManager");
17
+ const serviceManager_1 = require("./serviceManager");
18
+ const assetManager_1 = require("./assetManager");
19
+ const containerManager_1 = require("./containerManager");
20
+ const configManager_1 = require("./configManager");
21
+ const types_1 = require("./types");
22
+ const utils_1 = require("./utils/utils");
23
+ const operatorManager_1 = require("./operatorManager");
24
+ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
25
+ const definitionsManager_1 = require("./definitionsManager");
26
+ const taskManager_1 = require("./taskManager");
27
+ const CHECK_INTERVAL = 5000;
28
+ const DEFAULT_HEALTH_PORT_TYPE = 'http';
29
+ const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs - it failed
30
+ class InstanceManager {
31
+ _interval = undefined;
32
+ _instances = [];
33
+ instanceLocks = new async_lock_1.default();
34
+ constructor() {
35
+ this._instances = storageService_1.storageService.section('instances', []);
36
+ // We need to wait a bit before running the first check
37
+ this.checkInstancesLater(1000);
38
+ }
39
+ checkInstancesLater(time = CHECK_INTERVAL) {
40
+ if (this._interval) {
41
+ clearTimeout(this._interval);
42
+ }
43
+ this._interval = setTimeout(async () => {
44
+ await this.checkInstances();
45
+ this.checkInstancesLater();
46
+ }, time);
47
+ }
48
+ getInstances() {
49
+ if (!this._instances) {
50
+ return [];
51
+ }
52
+ return [...this._instances];
53
+ }
54
+ async getInstancesForPlan(systemId) {
55
+ if (!this._instances) {
56
+ return [];
57
+ }
58
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
59
+ const planInfo = await definitionsManager_1.definitionsManager.getDefinition(systemId);
60
+ if (!planInfo) {
61
+ return [];
62
+ }
63
+ const plan = planInfo.definition;
64
+ if (!plan?.spec?.blocks) {
65
+ return [];
66
+ }
67
+ const instanceIds = plan.spec?.blocks?.map((block) => block.id) || [];
68
+ return this._instances.filter((instance) => instance.systemId === systemId && instanceIds.includes(instance.instanceId));
69
+ }
70
+ getInstance(systemId, instanceId) {
71
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
72
+ return this._instances.find((i) => i.systemId === systemId && i.instanceId === instanceId);
73
+ }
74
+ async exclusive(systemId, instanceId, fn) {
75
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
76
+ const key = `${systemId}/${instanceId}`;
77
+ //console.log(`Acquiring lock for ${key}`, this.instanceLocks.isBusy(key));
78
+ const result = await this.instanceLocks.acquire(key, fn);
79
+ //console.log(`Releasing lock for ${key}`, this.instanceLocks.isBusy(key));
80
+ return result;
81
+ }
82
+ isLocked(systemId, instanceId) {
83
+ return this.instanceLocks.isBusy(`${systemId}/${instanceId}`);
84
+ }
85
+ async getLogs(systemId, instanceId) {
86
+ const instance = this.getInstance(systemId, instanceId);
87
+ if (!instance) {
88
+ throw new Error(`Instance ${systemId}/${instanceId} not found`);
89
+ }
90
+ switch (instance.type) {
91
+ case types_1.InstanceType.DOCKER:
92
+ return await containerManager_1.containerManager.getLogs(instance);
93
+ case types_1.InstanceType.UNKNOWN:
94
+ return [
95
+ {
96
+ level: 'INFO',
97
+ message: 'Instance is starting...',
98
+ time: Date.now(),
99
+ source: 'stdout',
100
+ },
101
+ ];
102
+ case types_1.InstanceType.LOCAL:
103
+ return [
104
+ {
105
+ level: 'INFO',
106
+ message: 'Instance started outside Kapeta - logs not available...',
107
+ time: Date.now(),
108
+ source: 'stdout',
109
+ },
110
+ ];
111
+ }
112
+ return [];
113
+ }
114
+ async saveInternalInstance(instance) {
115
+ instance.systemId = (0, nodejs_utils_1.normalizeKapetaUri)(instance.systemId);
116
+ if (instance.ref) {
117
+ instance.ref = (0, nodejs_utils_1.normalizeKapetaUri)(instance.ref);
118
+ }
119
+ //Get target address
120
+ let address = await serviceManager_1.serviceManager.getProviderAddress(instance.systemId, instance.instanceId, instance.portType ?? DEFAULT_HEALTH_PORT_TYPE);
121
+ const healthUrl = this.getHealthUrl(instance, address);
122
+ instance.address = address;
123
+ if (healthUrl) {
124
+ instance.health = healthUrl;
125
+ }
126
+ let existingInstance = this.getInstance(instance.systemId, instance.instanceId);
127
+ if (existingInstance) {
128
+ const ix = this._instances.indexOf(existingInstance);
129
+ this._instances.splice(ix, 1, instance);
130
+ socketManager_1.socketManager.emitSystemEvent(instance.systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
131
+ }
132
+ else {
133
+ this._instances.push(instance);
134
+ socketManager_1.socketManager.emitSystemEvent(instance.systemId, socketManager_1.EVENT_INSTANCE_CREATED, instance);
135
+ }
136
+ this.save();
137
+ return instance;
138
+ }
139
+ /**
140
+ * Method is called when instance is started from the Kapeta SDKs (e.g. NodeJS SDK)
141
+ * which self-registers with the cluster service locally on startup.
142
+ */
143
+ async registerInstanceFromSDK(systemId, instanceId, info) {
144
+ return this.exclusive(systemId, instanceId, async () => {
145
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
146
+ let instance = this.getInstance(systemId, instanceId);
147
+ //Get target address
148
+ const address = await serviceManager_1.serviceManager.getProviderAddress(systemId, instanceId, info.portType ?? DEFAULT_HEALTH_PORT_TYPE);
149
+ const healthUrl = this.getHealthUrl(info, address);
150
+ if (instance) {
151
+ if (instance.status === types_1.InstanceStatus.STOPPING &&
152
+ instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
153
+ //If instance is stopping do not interfere
154
+ return;
155
+ }
156
+ if (info.owner === types_1.InstanceOwner.EXTERNAL) {
157
+ //If instance was started externally - then we want to replace the internal instance with that
158
+ if (instance.owner === types_1.InstanceOwner.INTERNAL &&
159
+ (instance.status === types_1.InstanceStatus.READY ||
160
+ instance.status === types_1.InstanceStatus.STARTING ||
161
+ instance.status === types_1.InstanceStatus.UNHEALTHY)) {
162
+ throw new Error(`Instance ${instanceId} is already running`);
163
+ }
164
+ instance.desiredStatus = info.desiredStatus;
165
+ instance.owner = info.owner;
166
+ instance.status = types_1.InstanceStatus.STARTING;
167
+ instance.startedAt = Date.now();
168
+ }
169
+ instance.pid = info.pid;
170
+ instance.address = address;
171
+ if (info.type) {
172
+ instance.type = info.type;
173
+ }
174
+ if (healthUrl) {
175
+ instance.health = healthUrl;
176
+ }
177
+ if (info.portType) {
178
+ instance.portType = info.portType;
179
+ }
180
+ socketManager_1.socketManager.emitSystemEvent(systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
181
+ }
182
+ else {
183
+ //If instance was not found - then we're receiving an externally started instance
184
+ instance = {
185
+ ...info,
186
+ systemId,
187
+ instanceId,
188
+ status: types_1.InstanceStatus.STARTING,
189
+ startedAt: Date.now(),
190
+ desiredStatus: types_1.DesiredInstanceStatus.EXTERNAL,
191
+ owner: types_1.InstanceOwner.EXTERNAL,
192
+ health: healthUrl,
193
+ address,
194
+ };
195
+ this._instances.push(instance);
196
+ socketManager_1.socketManager.emitSystemEvent(systemId, socketManager_1.EVENT_INSTANCE_CREATED, instance);
197
+ }
198
+ this.save();
199
+ return instance;
200
+ });
201
+ }
202
+ getHealthUrl(info, address) {
203
+ let healthUrl = null;
204
+ let health = info.health ?? '/.kapeta/health';
205
+ if (health) {
206
+ if (health.startsWith('/')) {
207
+ health = health.substring(1);
208
+ }
209
+ healthUrl = address + health;
210
+ }
211
+ return healthUrl;
212
+ }
213
+ markAsStopped(systemId, instanceId) {
214
+ return this.exclusive(systemId, instanceId, async () => {
215
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
216
+ const instance = lodash_1.default.find(this._instances, { systemId, instanceId });
217
+ if (instance && instance.owner === types_1.InstanceOwner.EXTERNAL && instance.status !== types_1.InstanceStatus.STOPPED) {
218
+ if (instance.status != types_1.InstanceStatus.FAILED) {
219
+ instance.status = types_1.InstanceStatus.STOPPED;
220
+ }
221
+ instance.pid = null;
222
+ instance.health = null;
223
+ socketManager_1.socketManager.emitSystemEvent(systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
224
+ this.save();
225
+ }
226
+ });
227
+ }
228
+ async startAllForPlan(systemId) {
229
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
230
+ const plan = await assetManager_1.assetManager.getPlan(systemId, true);
231
+ if (!plan) {
232
+ throw new Error(`Plan not found: ${systemId}`);
233
+ }
234
+ if (!plan.spec.blocks) {
235
+ throw new Error(`No blocks found in plan: ${systemId}`);
236
+ }
237
+ return taskManager_1.taskManager.add(`plan:start:${systemId}`, async () => {
238
+ const promises = [];
239
+ const errors = [];
240
+ const instanceIds = await this.getAllInstancesExceptKind(systemId, types_1.KIND_BLOCK_TYPE_EXECUTABLE);
241
+ for (const instanceId of instanceIds) {
242
+ try {
243
+ promises.push(this.start(systemId, instanceId).then((taskOrInstance) => {
244
+ if (taskOrInstance instanceof taskManager_1.Task) {
245
+ return taskOrInstance.wait();
246
+ }
247
+ return taskOrInstance;
248
+ }));
249
+ }
250
+ catch (e) {
251
+ errors.push(e);
252
+ }
253
+ }
254
+ const settled = await Promise.allSettled(promises);
255
+ if (errors.length > 0) {
256
+ throw errors[0];
257
+ }
258
+ return settled
259
+ .map((p) => (p.status === 'fulfilled' ? p.value : null))
260
+ .filter((p) => !!p);
261
+ }, {
262
+ name: `Starting plan ${systemId}`,
263
+ });
264
+ }
265
+ stopAllForPlan(systemId) {
266
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
267
+ const instancesForPlan = this._instances.filter((instance) => instance.systemId === systemId);
268
+ return taskManager_1.taskManager.add(`plan:stop:${systemId}`, async () => {
269
+ return this.stopInstances(instancesForPlan);
270
+ }, {
271
+ name: `Stopping plan ${systemId}`,
272
+ });
273
+ }
274
+ async getInstanceOperator(systemId, instanceId, environment, ensureContainer = true) {
275
+ const blockInstance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
276
+ if (!blockInstance) {
277
+ throw new Error(`Instance not found: ${systemId}/${instanceId}`);
278
+ }
279
+ const blockRef = (0, nodejs_utils_1.normalizeKapetaUri)(blockInstance.block.ref);
280
+ const block = await assetManager_1.assetManager.getAsset(blockRef, true);
281
+ if (!block) {
282
+ throw new Error(`Block not found: ${blockRef}`);
283
+ }
284
+ const operatorDefinition = await definitionsManager_1.definitionsManager.getDefinition(block.kind);
285
+ if (!operatorDefinition) {
286
+ throw new Error(`Operator not found: ${block.kind}`);
287
+ }
288
+ if (operatorDefinition.definition.kind !== types_1.KIND_BLOCK_TYPE_OPERATOR) {
289
+ throw new Error(`Block is not an operator: ${blockRef}`);
290
+ }
291
+ if (!operatorDefinition.definition.spec.local) {
292
+ throw new Error(`Operator block has no local definition: ${blockRef}`);
293
+ }
294
+ const localConfig = operatorDefinition.definition.spec.local;
295
+ const ports = {};
296
+ if (ensureContainer) {
297
+ let instance = await this.start(systemId, instanceId);
298
+ if (instance instanceof taskManager_1.Task) {
299
+ instance = await instance.wait();
300
+ }
301
+ const container = await containerManager_1.containerManager.get(instance.pid);
302
+ if (!container) {
303
+ throw new Error(`Container not found: ${instance.pid}`);
304
+ }
305
+ const portInfo = await container.getPorts();
306
+ if (!portInfo) {
307
+ throw new Error(`No ports found for instance: ${instanceId}`);
308
+ }
309
+ Object.entries(portInfo).forEach(([key, value]) => {
310
+ ports[key] = {
311
+ protocol: value.protocol,
312
+ port: parseInt(value.hostPort),
313
+ };
314
+ });
315
+ }
316
+ else {
317
+ // If we're not ensuring the container is running we just get the ports from the local config
318
+ const instancePorts = await (0, utils_1.getOperatorInstancePorts)(systemId, instanceId, localConfig);
319
+ instancePorts.forEach((port) => {
320
+ ports[port.portType] = {
321
+ protocol: port.protocol,
322
+ port: port.hostPort,
323
+ };
324
+ });
325
+ }
326
+ const hostname = (0, utils_1.getRemoteHostForEnvironment)(environment);
327
+ return {
328
+ hostname,
329
+ ports,
330
+ credentials: localConfig.credentials,
331
+ options: localConfig.options,
332
+ };
333
+ }
334
+ async stop(systemId, instanceId) {
335
+ return this.stopInner(systemId, instanceId, true);
336
+ }
337
+ async stopInner(systemId, instanceId, changeDesired = false, checkForSingleton = true) {
338
+ if (checkForSingleton) {
339
+ const blockInstance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
340
+ const blockRef = (0, nodejs_utils_1.normalizeKapetaUri)(blockInstance.block.ref);
341
+ const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
342
+ if (!blockAsset) {
343
+ throw new Error('Block not found: ' + blockRef);
344
+ }
345
+ if (await this.isSingletonOperator(blockAsset)) {
346
+ const instances = await this.getAllInstancesForKind(systemId, blockAsset.data.kind);
347
+ if (instances.length > 1) {
348
+ const promises = instances.map((id) => {
349
+ return this.stopInner(systemId, id, changeDesired, false);
350
+ });
351
+ await Promise.all(promises);
352
+ return;
353
+ }
354
+ }
355
+ }
356
+ return this.exclusive(systemId, instanceId, async () => {
357
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
358
+ const instance = this.getInstance(systemId, instanceId);
359
+ if (!instance) {
360
+ return;
361
+ }
362
+ if (instance.status === types_1.InstanceStatus.STOPPED) {
363
+ return;
364
+ }
365
+ if (instance.status === types_1.InstanceStatus.STOPPING) {
366
+ return;
367
+ }
368
+ if (changeDesired && instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
369
+ instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
370
+ }
371
+ const wasFailed = instance.status === types_1.InstanceStatus.FAILED;
372
+ instance.status = types_1.InstanceStatus.STOPPING;
373
+ socketManager_1.socketManager.emitSystemEvent(systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
374
+ console.log('Stopping instance: %s::%s [desired: %s] [intentional: %s]', systemId, instanceId, instance.desiredStatus, changeDesired);
375
+ this.save();
376
+ try {
377
+ if (instance.type === 'docker') {
378
+ const containerName = await (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
379
+ const container = await containerManager_1.containerManager.getContainerByName(containerName);
380
+ if (container) {
381
+ try {
382
+ if (wasFailed) {
383
+ await container.remove();
384
+ }
385
+ else {
386
+ await container.stop();
387
+ }
388
+ instance.status = types_1.InstanceStatus.STOPPED;
389
+ socketManager_1.socketManager.emitSystemEvent(systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
390
+ this.save();
391
+ }
392
+ catch (e) {
393
+ console.error('Failed to stop container', e);
394
+ }
395
+ }
396
+ else {
397
+ console.warn('Container not found', containerName);
398
+ }
399
+ return;
400
+ }
401
+ if (!instance.pid) {
402
+ instance.status = types_1.InstanceStatus.STOPPED;
403
+ this.save();
404
+ return;
405
+ }
406
+ process.kill(instance.pid, 'SIGTERM');
407
+ instance.status = types_1.InstanceStatus.STOPPED;
408
+ socketManager_1.socketManager.emitSystemEvent(systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
409
+ this.save();
410
+ }
411
+ catch (e) {
412
+ console.error('Failed to stop process', e);
413
+ }
414
+ });
415
+ }
416
+ async start(systemId, instanceId, checkForSingleton = true) {
417
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
418
+ const blockInstance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
419
+ const blockRef = (0, nodejs_utils_1.normalizeKapetaUri)(blockInstance.block.ref);
420
+ const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
421
+ if (!blockAsset) {
422
+ throw new Error('Block not found: ' + blockRef);
423
+ }
424
+ if (checkForSingleton && (await this.isSingletonOperator(blockAsset))) {
425
+ const instances = await this.getAllInstancesForKind(systemId, blockAsset.data.kind);
426
+ if (instances.length > 1) {
427
+ const promises = instances.map((id) => {
428
+ return this.start(systemId, id, false);
429
+ });
430
+ await Promise.all(promises);
431
+ return promises[0];
432
+ }
433
+ }
434
+ return this.exclusive(systemId, instanceId, async () => {
435
+ let existingInstance = this.getInstance(systemId, instanceId);
436
+ if (existingInstance && existingInstance.pid) {
437
+ const container = await containerManager_1.containerManager.get(existingInstance.pid);
438
+ if (!container) {
439
+ // The container is not running
440
+ existingInstance = undefined;
441
+ }
442
+ }
443
+ if (existingInstance && existingInstance.pid) {
444
+ if (existingInstance.status === types_1.InstanceStatus.READY) {
445
+ // Instance is already running
446
+ return existingInstance;
447
+ }
448
+ if (existingInstance.desiredStatus === types_1.DesiredInstanceStatus.RUN &&
449
+ existingInstance.status === types_1.InstanceStatus.STARTING) {
450
+ // Internal instance is already starting - don't start it again
451
+ return existingInstance;
452
+ }
453
+ if (existingInstance.owner === types_1.InstanceOwner.EXTERNAL &&
454
+ existingInstance.status === types_1.InstanceStatus.STARTING) {
455
+ // External instance is already starting - don't start it again
456
+ return existingInstance;
457
+ }
458
+ }
459
+ let instance = {
460
+ systemId,
461
+ instanceId,
462
+ ref: blockRef,
463
+ name: blockAsset.data.metadata.name,
464
+ desiredStatus: types_1.DesiredInstanceStatus.RUN,
465
+ owner: types_1.InstanceOwner.INTERNAL,
466
+ type: existingInstance?.type ?? types_1.InstanceType.UNKNOWN,
467
+ status: types_1.InstanceStatus.STARTING,
468
+ startedAt: Date.now(),
469
+ };
470
+ console.log('Starting instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
471
+ // Save the instance before starting it, so that we can track the status
472
+ await this.saveInternalInstance(instance);
473
+ const blockSpec = blockAsset.data.spec;
474
+ if (blockSpec.consumers) {
475
+ const promises = blockSpec.consumers.map(async (consumer) => {
476
+ const consumerUri = (0, nodejs_utils_1.parseKapetaUri)(consumer.kind);
477
+ const asset = await definitionsManager_1.definitionsManager.getDefinition(consumer.kind);
478
+ if (!asset) {
479
+ // Definition not found
480
+ return Promise.resolve();
481
+ }
482
+ if (types_1.KIND_RESOURCE_OPERATOR.toLowerCase() !== asset.definition.kind.toLowerCase()) {
483
+ // Not an operator
484
+ return Promise.resolve();
485
+ }
486
+ // Check if the operator has a local definition, if not we skip it since we can't start it
487
+ if (!asset.definition.spec.local) {
488
+ console.log('Skipping operator since it as no local definition: %s', consumer.kind);
489
+ return Promise.resolve();
490
+ }
491
+ console.log('Ensuring resource: %s in %s', consumerUri.id, systemId);
492
+ return operatorManager_1.operatorManager.ensureOperator(systemId, consumerUri.fullName, consumerUri.version);
493
+ });
494
+ await Promise.all(promises);
495
+ }
496
+ if (existingInstance) {
497
+ // Check if the instance is already running - but after we've commmuicated the desired status
498
+ const currentStatus = await this.requestInstanceStatus(existingInstance);
499
+ if (currentStatus === types_1.InstanceStatus.READY) {
500
+ // Instance is already running
501
+ return existingInstance;
502
+ }
503
+ }
504
+ const resolvedConfig = await configManager_1.configManager.getConfigForBlockInstance(systemId, instanceId);
505
+ const task = taskManager_1.taskManager.add(`instance:start:${systemId}:${instanceId}`, async () => {
506
+ const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
507
+ const startTime = Date.now();
508
+ try {
509
+ const processInfo = await runner.start(blockRef, instanceId, resolvedConfig);
510
+ instance.status = types_1.InstanceStatus.STARTING;
511
+ return this.saveInternalInstance({
512
+ ...instance,
513
+ type: processInfo.type,
514
+ pid: processInfo.pid ?? -1,
515
+ health: null,
516
+ portType: processInfo.portType,
517
+ status: types_1.InstanceStatus.STARTING,
518
+ });
519
+ }
520
+ catch (e) {
521
+ console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e);
522
+ const logs = [
523
+ {
524
+ source: 'stdout',
525
+ level: 'ERROR',
526
+ message: e.message,
527
+ time: Date.now(),
528
+ },
529
+ ];
530
+ const out = await this.saveInternalInstance({
531
+ ...instance,
532
+ type: types_1.InstanceType.DOCKER,
533
+ health: null,
534
+ portType: DEFAULT_HEALTH_PORT_TYPE,
535
+ status: types_1.InstanceStatus.FAILED,
536
+ errorMessage: e.message ?? 'Failed to start - Check logs for details.',
537
+ });
538
+ socketManager_1.socketManager.emitInstanceLog(systemId, instanceId, logs[0]);
539
+ return out;
540
+ }
541
+ }, {
542
+ name: `Starting instance: ${instance.name}`,
543
+ systemId,
544
+ });
545
+ return task;
546
+ });
547
+ }
548
+ /**
549
+ * Stops an instance but does not remove it from the list of active instances
550
+ *
551
+ * It will be started again next time the system checks the status of the instance
552
+ *
553
+ * We do it this way to not cause the user to wait for the instance to start again
554
+ */
555
+ async prepareForRestart(systemId, instanceId) {
556
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
557
+ console.log('Stopping instance during restart...', systemId, instanceId);
558
+ await this.stopInner(systemId, instanceId);
559
+ }
560
+ async stopAll() {
561
+ return this.stopInstances(this._instances);
562
+ }
563
+ async stopInstances(instances) {
564
+ const promises = instances.map((instance) => this.stop(instance.systemId, instance.instanceId));
565
+ await Promise.allSettled(promises);
566
+ this.save();
567
+ }
568
+ save() {
569
+ try {
570
+ storageService_1.storageService.put('instances', this._instances.map((instance) => {
571
+ return { ...instance };
572
+ }));
573
+ }
574
+ catch (e) {
575
+ console.error('Failed to save instances', this._instances, e);
576
+ }
577
+ }
578
+ async checkInstances() {
579
+ //console.log('\n## Checking instances:');
580
+ let changed = false;
581
+ const all = [...this._instances];
582
+ while (all.length > 0) {
583
+ // Check a few instances at a time - docker doesn't like too many concurrent requests
584
+ const chunk = all.splice(0, 30);
585
+ const promises = chunk.map(async (oldInstance) => {
586
+ if (!oldInstance.systemId) {
587
+ return;
588
+ }
589
+ // Grab the latest here
590
+ const instance = this.getInstance(oldInstance.systemId, oldInstance.instanceId);
591
+ if (!instance) {
592
+ return;
593
+ }
594
+ instance.systemId = (0, nodejs_utils_1.normalizeKapetaUri)(instance.systemId);
595
+ if (instance.ref) {
596
+ instance.ref = (0, nodejs_utils_1.normalizeKapetaUri)(instance.ref);
597
+ }
598
+ if (instance.desiredStatus === types_1.DesiredInstanceStatus.RUN) {
599
+ // Check if the plan still exists and the instance is still in the plan
600
+ // - and that the block definition exists
601
+ try {
602
+ const plan = await assetManager_1.assetManager.getAsset(instance.systemId, true, false);
603
+ if (!plan) {
604
+ console.log('Plan not found - reset to stop', instance.ref, instance.systemId);
605
+ instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
606
+ changed = true;
607
+ return;
608
+ }
609
+ const planData = plan.data;
610
+ const planInstance = planData?.spec?.blocks?.find((b) => b.id === instance.instanceId);
611
+ if (!planInstance || !planInstance?.block?.ref) {
612
+ console.log('Plan instance not found - reset to stop', instance.ref, instance.systemId);
613
+ instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
614
+ changed = true;
615
+ return;
616
+ }
617
+ const blockDef = await assetManager_1.assetManager.getAsset(instance.ref, true, false);
618
+ if (!blockDef) {
619
+ console.log('Block definition not found - reset to stop', instance.ref, instance.systemId);
620
+ instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
621
+ changed = true;
622
+ return;
623
+ }
624
+ }
625
+ catch (e) {
626
+ console.warn('Failed to check assets', instance.systemId, e);
627
+ instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
628
+ return;
629
+ }
630
+ }
631
+ const newStatus = await this.requestInstanceStatus(instance);
632
+ /*
633
+ console.log('Check instance %s %s: [current: %s, new: %s, desired: %s]',
634
+ instance.systemId, instance.instanceId, instance.status, newStatus, instance.desiredStatus);
635
+ */
636
+ if (newStatus === types_1.InstanceStatus.BUSY) {
637
+ // If instance is busy we skip it
638
+ //console.log('Instance %s %s is busy', instance.systemId, instance.instanceId);
639
+ return;
640
+ }
641
+ if (instance.startedAt !== undefined &&
642
+ newStatus === types_1.InstanceStatus.UNHEALTHY &&
643
+ instance.startedAt + containerManager_1.HEALTH_CHECK_TIMEOUT < Date.now() &&
644
+ instance.status === types_1.InstanceStatus.STARTING) {
645
+ // If instance is starting we consider unhealthy an indication
646
+ // that it is still starting
647
+ //console.log('Instance %s %s is still starting', instance.systemId, instance.instanceId);
648
+ return;
649
+ }
650
+ if (instance.status !== newStatus) {
651
+ const oldStatus = instance.status;
652
+ const skipUpdate = ([types_1.InstanceStatus.READY, types_1.InstanceStatus.UNHEALTHY].includes(newStatus) &&
653
+ instance.status === types_1.InstanceStatus.STOPPING) ||
654
+ (newStatus === types_1.InstanceStatus.STOPPED &&
655
+ instance.status === types_1.InstanceStatus.STARTING &&
656
+ instance.desiredStatus === types_1.DesiredInstanceStatus.RUN);
657
+ if (!skipUpdate) {
658
+ const oldStatus = instance.status;
659
+ instance.status = newStatus;
660
+ console.log('Instance status changed: %s %s: %s -> %s', instance.systemId, instance.instanceId, oldStatus, instance.status);
661
+ socketManager_1.socketManager.emitSystemEvent(instance.systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
662
+ changed = true;
663
+ }
664
+ }
665
+ if (instance.desiredStatus === types_1.DesiredInstanceStatus.RUN &&
666
+ [types_1.InstanceStatus.STOPPED, types_1.InstanceStatus.STOPPING].includes(newStatus)) {
667
+ //If the instance is stopped but we want it to run, start it
668
+ try {
669
+ await this.start(instance.systemId, instance.instanceId);
670
+ }
671
+ catch (e) {
672
+ console.warn('Failed to start previously stopped instance', instance.systemId, instance.instanceId, e);
673
+ }
674
+ return;
675
+ }
676
+ if (instance.desiredStatus === types_1.DesiredInstanceStatus.STOP &&
677
+ [types_1.InstanceStatus.READY, types_1.InstanceStatus.STARTING, types_1.InstanceStatus.UNHEALTHY].includes(newStatus)) {
678
+ //If the instance is running but we want it to stop, stop it
679
+ try {
680
+ console.log('Stopping instance since it is its desired state', instance.systemId, instance.instanceId);
681
+ await this.stopInner(instance.systemId, instance.instanceId);
682
+ }
683
+ catch (e) {
684
+ console.warn('Failed to stop instance', instance.systemId, instance.instanceId, e);
685
+ }
686
+ return;
687
+ }
688
+ if (instance.desiredStatus === types_1.DesiredInstanceStatus.RUN &&
689
+ instance.status !== newStatus &&
690
+ newStatus === types_1.InstanceStatus.UNHEALTHY) {
691
+ //If the instance is unhealthy, try to restart it
692
+ console.log('Restarting unhealthy instance', instance);
693
+ try {
694
+ await this.prepareForRestart(instance.systemId, instance.instanceId);
695
+ }
696
+ catch (e) {
697
+ console.warn('Failed to restart instance', instance.systemId, instance.instanceId, e);
698
+ }
699
+ }
700
+ });
701
+ await Promise.allSettled(promises);
702
+ }
703
+ if (changed) {
704
+ this.save();
705
+ }
706
+ //console.log('\n##\n');
707
+ }
708
+ async getExternalStatus(instance) {
709
+ if (instance.type === types_1.InstanceType.DOCKER) {
710
+ const containerName = await (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
711
+ const container = await containerManager_1.containerManager.getContainerByName(containerName);
712
+ if (!container) {
713
+ // If the container doesn't exist, we consider the instance stopped
714
+ return types_1.InstanceStatus.STOPPED;
715
+ }
716
+ const state = await container.status();
717
+ if (!state) {
718
+ return types_1.InstanceStatus.STOPPED;
719
+ }
720
+ const statusType = state.Status;
721
+ if (statusType === 'running') {
722
+ if (state.Health?.Status) {
723
+ const healthStatusType = state.Health.Status;
724
+ if (healthStatusType === 'healthy' || healthStatusType === 'none') {
725
+ return types_1.InstanceStatus.READY;
726
+ }
727
+ if (healthStatusType === 'starting') {
728
+ return types_1.InstanceStatus.STARTING;
729
+ }
730
+ if (healthStatusType === 'unhealthy') {
731
+ return types_1.InstanceStatus.UNHEALTHY;
732
+ }
733
+ }
734
+ return types_1.InstanceStatus.READY;
735
+ }
736
+ if (statusType === 'created') {
737
+ if (state.ExitCode !== undefined && state.ExitCode !== 0) {
738
+ // Failed during creation. Exit code is not always reliable though
739
+ if (state.Error) {
740
+ return types_1.InstanceStatus.FAILED;
741
+ }
742
+ else {
743
+ return types_1.InstanceStatus.STOPPED;
744
+ }
745
+ }
746
+ return types_1.InstanceStatus.STARTING;
747
+ }
748
+ if (statusType === 'exited' || statusType === 'dead') {
749
+ if (!state.Error) {
750
+ // Exit code is not always reliable - if there is no error we assume it's stopped
751
+ return types_1.InstanceStatus.STOPPED;
752
+ }
753
+ return types_1.InstanceStatus.FAILED;
754
+ }
755
+ if (statusType === 'removing') {
756
+ return types_1.InstanceStatus.BUSY;
757
+ }
758
+ if (statusType === 'restarting') {
759
+ return types_1.InstanceStatus.BUSY;
760
+ }
761
+ if (statusType === 'paused') {
762
+ return types_1.InstanceStatus.BUSY;
763
+ }
764
+ return types_1.InstanceStatus.STOPPED;
765
+ }
766
+ if (!instance.pid) {
767
+ return types_1.InstanceStatus.STOPPED;
768
+ }
769
+ //Otherwise its just a normal process.
770
+ //TODO: Handle for Windows
771
+ try {
772
+ if (process.kill(instance.pid, 0)) {
773
+ return types_1.InstanceStatus.READY;
774
+ }
775
+ }
776
+ catch (err) {
777
+ if (err.code === 'EPERM') {
778
+ return types_1.InstanceStatus.READY;
779
+ }
780
+ }
781
+ return types_1.InstanceStatus.STOPPED;
782
+ }
783
+ async requestInstanceStatus(instance) {
784
+ const externalStatus = await this.getExternalStatus(instance);
785
+ if (instance.type === types_1.InstanceType.DOCKER) {
786
+ // For docker instances we can rely on docker status
787
+ return externalStatus;
788
+ }
789
+ if (externalStatus === types_1.InstanceStatus.STOPPED) {
790
+ return externalStatus;
791
+ }
792
+ if (!instance.health) {
793
+ //No health url means we assume it's healthy as soon as it's running
794
+ return types_1.InstanceStatus.READY;
795
+ }
796
+ return new Promise((resolve) => {
797
+ if (!instance.health) {
798
+ resolve(types_1.InstanceStatus.READY);
799
+ return;
800
+ }
801
+ (0, request_1.default)(instance.health, (err, response) => {
802
+ if (err) {
803
+ resolve(types_1.InstanceStatus.UNHEALTHY);
804
+ return;
805
+ }
806
+ if (response.statusCode > 399) {
807
+ resolve(types_1.InstanceStatus.UNHEALTHY);
808
+ return;
809
+ }
810
+ resolve(types_1.InstanceStatus.READY);
811
+ });
812
+ });
813
+ }
814
+ async isSingletonOperator(blockAsset) {
815
+ const provider = await assetManager_1.assetManager.getAsset(blockAsset.data.kind);
816
+ if (!provider) {
817
+ return false;
818
+ }
819
+ if ((0, nodejs_utils_1.parseKapetaUri)(provider.kind).fullName === types_1.KIND_BLOCK_TYPE_OPERATOR) {
820
+ const localConfig = provider.data.spec.local;
821
+ return localConfig.singleton ?? false;
822
+ }
823
+ return false;
824
+ }
825
+ async getKindForAssetRef(assetRef) {
826
+ const block = await assetManager_1.assetManager.getAsset(assetRef);
827
+ if (!block) {
828
+ return null;
829
+ }
830
+ return block.data.kind;
831
+ }
832
+ /**
833
+ * Get the kind of an asset. Use the maxDepth parameter to specify how deep to look for the
834
+ * kind. For example, if maxDepth is 2, the method will look for the kind of the asset and then
835
+ * the kind of the kind.
836
+ * @param assetRef The asset reference
837
+ * @param maxDepth The maximum depth to look for the kind
838
+ * @returns The kind of the asset or null if not found
839
+ */
840
+ async getDeepKindForAssetRef(assetRef, maxDepth) {
841
+ if (maxDepth <= 0) {
842
+ return null;
843
+ }
844
+ try {
845
+ const asset = await assetManager_1.assetManager.getAsset(assetRef);
846
+ if (!asset || !asset.data.kind) {
847
+ return null;
848
+ }
849
+ if (maxDepth === 1) {
850
+ return asset.data.kind;
851
+ }
852
+ else {
853
+ // Recurse with the kind of the current block and one less depth
854
+ return await this.getDeepKindForAssetRef(asset.data.kind, maxDepth - 1);
855
+ }
856
+ }
857
+ catch (error) {
858
+ console.error('Error fetching kind for assetRef:', assetRef, error);
859
+ return null;
860
+ }
861
+ }
862
+ async isUsingKind(ref, kind) {
863
+ const assetKind = await this.getKindForAssetRef(ref);
864
+ if (!assetKind) {
865
+ return false;
866
+ }
867
+ return (0, nodejs_utils_1.parseKapetaUri)(assetKind).fullName === (0, nodejs_utils_1.parseKapetaUri)(kind).fullName;
868
+ }
869
+ async getAllInstancesForKind(systemId, kind) {
870
+ const plan = await assetManager_1.assetManager.getPlan(systemId);
871
+ if (!plan?.spec?.blocks) {
872
+ return [];
873
+ }
874
+ const out = [];
875
+ for (const block of plan.spec.blocks) {
876
+ if (await this.isUsingKind(block.block.ref, kind)) {
877
+ out.push(block.id);
878
+ }
879
+ }
880
+ return out;
881
+ }
882
+ /**
883
+ * Get the ids for all block instances except the ones of the specified kind
884
+ * @param systemId The plan reference id
885
+ * @param kind The kind to exclude. Can be a string or an array of strings
886
+ * @returns An array of block instance ids
887
+ */
888
+ async getAllInstancesExceptKind(systemId, kind) {
889
+ const plan = await assetManager_1.assetManager.getPlan(systemId);
890
+ if (!plan?.spec?.blocks) {
891
+ return [];
892
+ }
893
+ const out = [];
894
+ const excludedKinds = kind instanceof Array ? kind : [kind];
895
+ for (const block of plan.spec.blocks) {
896
+ const blockKindOfKind = await this.getDeepKindForAssetRef(block.block.ref, 2);
897
+ if (!blockKindOfKind) {
898
+ continue;
899
+ }
900
+ const shouldIncludeBlock = excludedKinds.some((excludedKind) => excludedKind === (0, nodejs_utils_1.parseKapetaUri)(blockKindOfKind).fullName) ===
901
+ false;
902
+ if (shouldIncludeBlock) {
903
+ out.push(block.id);
904
+ }
905
+ }
906
+ return out;
907
+ }
908
+ }
909
+ exports.InstanceManager = InstanceManager;
910
+ exports.instanceManager = new InstanceManager();