@alwaysai/device-agent 1.5.0 → 2.0.1

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 (305) hide show
  1. package/lib/application-control/config.d.ts.map +1 -1
  2. package/lib/application-control/config.js +8 -3
  3. package/lib/application-control/config.js.map +1 -1
  4. package/lib/application-control/environment-variables.d.ts +5 -5
  5. package/lib/application-control/environment-variables.d.ts.map +1 -1
  6. package/lib/application-control/environment-variables.js +25 -38
  7. package/lib/application-control/environment-variables.js.map +1 -1
  8. package/lib/application-control/environment-variables.test.js +27 -7
  9. package/lib/application-control/environment-variables.test.js.map +1 -1
  10. package/lib/application-control/index.d.ts +4 -4
  11. package/lib/application-control/index.d.ts.map +1 -1
  12. package/lib/application-control/index.js +1 -4
  13. package/lib/application-control/index.js.map +1 -1
  14. package/lib/application-control/install.d.ts +4 -1
  15. package/lib/application-control/install.d.ts.map +1 -1
  16. package/lib/application-control/install.js +24 -8
  17. package/lib/application-control/install.js.map +1 -1
  18. package/lib/application-control/models.d.ts +0 -11
  19. package/lib/application-control/models.d.ts.map +1 -1
  20. package/lib/application-control/models.js +5 -54
  21. package/lib/application-control/models.js.map +1 -1
  22. package/lib/application-control/utils.d.ts +0 -4
  23. package/lib/application-control/utils.d.ts.map +1 -1
  24. package/lib/application-control/utils.js +12 -22
  25. package/lib/application-control/utils.js.map +1 -1
  26. package/lib/cloud-connection/base-message-handler.d.ts +27 -0
  27. package/lib/cloud-connection/base-message-handler.d.ts.map +1 -0
  28. package/lib/cloud-connection/base-message-handler.js +72 -0
  29. package/lib/cloud-connection/base-message-handler.js.map +1 -0
  30. package/lib/cloud-connection/bootstrap-provision.js +3 -2
  31. package/lib/cloud-connection/bootstrap-provision.js.map +1 -1
  32. package/lib/cloud-connection/connection-manager.d.ts +21 -0
  33. package/lib/cloud-connection/connection-manager.d.ts.map +1 -0
  34. package/lib/cloud-connection/connection-manager.js +158 -0
  35. package/lib/cloud-connection/connection-manager.js.map +1 -0
  36. package/lib/cloud-connection/device-agent-cloud-connection.d.ts +9 -30
  37. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  38. package/lib/cloud-connection/device-agent-cloud-connection.js +69 -508
  39. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  40. package/lib/cloud-connection/device-agent-message-handler.d.ts +22 -0
  41. package/lib/cloud-connection/device-agent-message-handler.d.ts.map +1 -0
  42. package/lib/cloud-connection/device-agent-message-handler.js +357 -0
  43. package/lib/cloud-connection/device-agent-message-handler.js.map +1 -0
  44. package/lib/cloud-connection/device-agent.d.ts.map +1 -1
  45. package/lib/cloud-connection/device-agent.js +11 -9
  46. package/lib/cloud-connection/device-agent.js.map +1 -1
  47. package/lib/cloud-connection/live-updates-handler.d.ts +19 -28
  48. package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
  49. package/lib/cloud-connection/live-updates-handler.js +60 -172
  50. package/lib/cloud-connection/live-updates-handler.js.map +1 -1
  51. package/lib/cloud-connection/live-updates-handler.test.js +71 -165
  52. package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
  53. package/lib/cloud-connection/message-dispatcher.d.ts +10 -0
  54. package/lib/cloud-connection/message-dispatcher.d.ts.map +1 -0
  55. package/lib/cloud-connection/message-dispatcher.js +27 -0
  56. package/lib/cloud-connection/message-dispatcher.js.map +1 -0
  57. package/lib/cloud-connection/passthrough-handler.d.ts +4 -1
  58. package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
  59. package/lib/cloud-connection/passthrough-handler.js +30 -11
  60. package/lib/cloud-connection/passthrough-handler.js.map +1 -1
  61. package/lib/cloud-connection/shadow-handler.d.ts +11 -3
  62. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  63. package/lib/cloud-connection/shadow-handler.js +133 -28
  64. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  65. package/lib/cloud-connection/shadow-handler.test.js +45 -57
  66. package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
  67. package/lib/cloud-connection/shadow.d.ts.map +1 -1
  68. package/lib/cloud-connection/shadow.js +2 -1
  69. package/lib/cloud-connection/shadow.js.map +1 -1
  70. package/lib/cloud-connection/transaction-manager.d.ts +12 -3
  71. package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
  72. package/lib/cloud-connection/transaction-manager.js +29 -28
  73. package/lib/cloud-connection/transaction-manager.js.map +1 -1
  74. package/lib/cloud-connection/transaction-manager.test.js +46 -5
  75. package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
  76. package/lib/device-control/device-control.d.ts +8 -8
  77. package/lib/device-control/device-control.d.ts.map +1 -1
  78. package/lib/device-control/device-control.js +95 -71
  79. package/lib/device-control/device-control.js.map +1 -1
  80. package/lib/docker/docker-compose.d.ts.map +1 -1
  81. package/lib/docker/docker-compose.js +2 -1
  82. package/lib/docker/docker-compose.js.map +1 -1
  83. package/lib/infrastructure/agent-config.d.ts +2 -1
  84. package/lib/infrastructure/agent-config.d.ts.map +1 -1
  85. package/lib/infrastructure/agent-config.js +7 -7
  86. package/lib/infrastructure/agent-config.js.map +1 -1
  87. package/lib/infrastructure/agent-config.test.js +3 -1
  88. package/lib/infrastructure/agent-config.test.js.map +1 -1
  89. package/lib/infrastructure/config-check-utility.d.ts +6 -0
  90. package/lib/infrastructure/config-check-utility.d.ts.map +1 -0
  91. package/lib/infrastructure/config-check-utility.js +67 -0
  92. package/lib/infrastructure/config-check-utility.js.map +1 -0
  93. package/lib/infrastructure/config-check-utility.test.d.ts +2 -0
  94. package/lib/infrastructure/config-check-utility.test.d.ts.map +1 -0
  95. package/lib/infrastructure/config-check-utility.test.js +109 -0
  96. package/lib/infrastructure/config-check-utility.test.js.map +1 -0
  97. package/lib/infrastructure/device-certificate.d.ts +10 -0
  98. package/lib/infrastructure/device-certificate.d.ts.map +1 -0
  99. package/lib/infrastructure/device-certificate.js +47 -0
  100. package/lib/infrastructure/device-certificate.js.map +1 -0
  101. package/lib/infrastructure/device-certificate.test.d.ts +2 -0
  102. package/lib/infrastructure/device-certificate.test.d.ts.map +1 -0
  103. package/lib/infrastructure/device-certificate.test.js +24 -0
  104. package/lib/infrastructure/device-certificate.test.js.map +1 -0
  105. package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts +2 -0
  106. package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts.map +1 -0
  107. package/lib/infrastructure/legacy-migration/legacy-file.test.js +61 -0
  108. package/lib/infrastructure/legacy-migration/legacy-file.test.js.map +1 -0
  109. package/lib/infrastructure/legacy-migration/legacy-files.d.ts +75 -0
  110. package/lib/infrastructure/legacy-migration/legacy-files.d.ts.map +1 -0
  111. package/lib/infrastructure/legacy-migration/legacy-files.js +75 -0
  112. package/lib/infrastructure/legacy-migration/legacy-files.js.map +1 -0
  113. package/lib/infrastructure/legacy-migration/legacy-migration.d.ts +6 -0
  114. package/lib/infrastructure/legacy-migration/legacy-migration.d.ts.map +1 -0
  115. package/lib/infrastructure/legacy-migration/legacy-migration.js +149 -0
  116. package/lib/infrastructure/legacy-migration/legacy-migration.js.map +1 -0
  117. package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts +2 -0
  118. package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts.map +1 -0
  119. package/lib/infrastructure/legacy-migration/legacy-migration.test.js +226 -0
  120. package/lib/infrastructure/legacy-migration/legacy-migration.test.js.map +1 -0
  121. package/lib/infrastructure/require-files-present-ready.test.d.ts +2 -0
  122. package/lib/infrastructure/require-files-present-ready.test.d.ts.map +1 -0
  123. package/lib/infrastructure/require-files-present-ready.test.js +44 -0
  124. package/lib/infrastructure/require-files-present-ready.test.js.map +1 -0
  125. package/lib/infrastructure/required-config-checks.d.ts +2 -0
  126. package/lib/infrastructure/required-config-checks.d.ts.map +1 -0
  127. package/lib/infrastructure/required-config-checks.js +30 -0
  128. package/lib/infrastructure/required-config-checks.js.map +1 -0
  129. package/lib/infrastructure/tokens-and-device-cfg.d.ts.map +1 -1
  130. package/lib/infrastructure/tokens-and-device-cfg.js +11 -8
  131. package/lib/infrastructure/tokens-and-device-cfg.js.map +1 -1
  132. package/lib/jobs/job-handler.d.ts +23 -0
  133. package/lib/jobs/job-handler.d.ts.map +1 -0
  134. package/lib/jobs/job-handler.js +131 -0
  135. package/lib/jobs/job-handler.js.map +1 -0
  136. package/lib/local-connection/rabbitmq-connection.d.ts.map +1 -1
  137. package/lib/local-connection/rabbitmq-connection.js +14 -14
  138. package/lib/local-connection/rabbitmq-connection.js.map +1 -1
  139. package/lib/secure-tunneling/secure-tunnel-message-handler.d.ts +8 -0
  140. package/lib/secure-tunneling/secure-tunnel-message-handler.d.ts.map +1 -0
  141. package/lib/secure-tunneling/secure-tunnel-message-handler.js +42 -0
  142. package/lib/secure-tunneling/secure-tunnel-message-handler.js.map +1 -0
  143. package/lib/secure-tunneling/secure-tunneling.d.ts +9 -9
  144. package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -1
  145. package/lib/secure-tunneling/secure-tunneling.js +21 -16
  146. package/lib/secure-tunneling/secure-tunneling.js.map +1 -1
  147. package/lib/secure-tunneling/secure-tunneling.test.js +11 -13
  148. package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -1
  149. package/lib/subcommands/app/analytics.d.ts.map +1 -1
  150. package/lib/subcommands/app/analytics.js +1 -2
  151. package/lib/subcommands/app/analytics.js.map +1 -1
  152. package/lib/subcommands/app/env-vars.d.ts +4 -0
  153. package/lib/subcommands/app/env-vars.d.ts.map +1 -1
  154. package/lib/subcommands/app/env-vars.js +52 -6
  155. package/lib/subcommands/app/env-vars.js.map +1 -1
  156. package/lib/subcommands/app/index.d.ts.map +1 -1
  157. package/lib/subcommands/app/index.js +1 -3
  158. package/lib/subcommands/app/index.js.map +1 -1
  159. package/lib/subcommands/app/models.d.ts +0 -11
  160. package/lib/subcommands/app/models.d.ts.map +1 -1
  161. package/lib/subcommands/app/models.js +2 -58
  162. package/lib/subcommands/app/models.js.map +1 -1
  163. package/lib/subcommands/app/shadow.d.ts.map +1 -1
  164. package/lib/subcommands/app/shadow.js +6 -5
  165. package/lib/subcommands/app/shadow.js.map +1 -1
  166. package/lib/subcommands/app/version.d.ts +2 -0
  167. package/lib/subcommands/app/version.d.ts.map +1 -1
  168. package/lib/subcommands/app/version.js +16 -6
  169. package/lib/subcommands/app/version.js.map +1 -1
  170. package/lib/subcommands/config.d.ts +2 -0
  171. package/lib/subcommands/config.d.ts.map +1 -0
  172. package/lib/subcommands/config.js +39 -0
  173. package/lib/subcommands/config.js.map +1 -0
  174. package/lib/subcommands/device/clean.d.ts +1 -1
  175. package/lib/subcommands/device/clean.d.ts.map +1 -1
  176. package/lib/subcommands/device/clean.js +23 -13
  177. package/lib/subcommands/device/clean.js.map +1 -1
  178. package/lib/subcommands/device/index.d.ts.map +1 -1
  179. package/lib/subcommands/device/index.js +3 -1
  180. package/lib/subcommands/device/index.js.map +1 -1
  181. package/lib/subcommands/device/init.js +8 -8
  182. package/lib/subcommands/device/init.js.map +1 -1
  183. package/lib/subcommands/device/migrate.d.ts +2 -0
  184. package/lib/subcommands/device/migrate.d.ts.map +1 -0
  185. package/lib/subcommands/device/migrate.js +24 -0
  186. package/lib/subcommands/device/migrate.js.map +1 -0
  187. package/lib/subcommands/device/refresh.d.ts.map +1 -1
  188. package/lib/subcommands/device/refresh.js +1 -0
  189. package/lib/subcommands/device/refresh.js.map +1 -1
  190. package/lib/subcommands/index.d.ts +1 -1
  191. package/lib/subcommands/index.d.ts.map +1 -1
  192. package/lib/subcommands/index.js +3 -1
  193. package/lib/subcommands/index.js.map +1 -1
  194. package/lib/subcommands/rabbitmq-connection.d.ts +1 -1
  195. package/lib/subcommands/rabbitmq-connection.d.ts.map +1 -1
  196. package/lib/util/aai-error.d.ts +12 -0
  197. package/lib/util/aai-error.d.ts.map +1 -0
  198. package/lib/util/aai-error.js +11 -0
  199. package/lib/util/aai-error.js.map +1 -0
  200. package/lib/util/aws-regions.d.ts +2 -0
  201. package/lib/util/aws-regions.d.ts.map +1 -0
  202. package/lib/util/{cloud-mode-ready.js → aws-regions.js} +2 -20
  203. package/lib/util/aws-regions.js.map +1 -0
  204. package/lib/util/check-for-updates.d.ts.map +1 -1
  205. package/lib/util/check-for-updates.js +5 -28
  206. package/lib/util/check-for-updates.js.map +1 -1
  207. package/lib/util/clean-certs.d.ts.map +1 -1
  208. package/lib/util/clean-certs.js +5 -4
  209. package/lib/util/clean-certs.js.map +1 -1
  210. package/lib/util/directories.d.ts +4 -18
  211. package/lib/util/directories.d.ts.map +1 -1
  212. package/lib/util/directories.js +18 -32
  213. package/lib/util/directories.js.map +1 -1
  214. package/lib/util/file.d.ts +4 -0
  215. package/lib/util/file.d.ts.map +1 -1
  216. package/lib/util/file.js +65 -4
  217. package/lib/util/file.js.map +1 -1
  218. package/lib/util/get-device-id.d.ts.map +1 -1
  219. package/lib/util/get-device-id.js +7 -1
  220. package/lib/util/get-device-id.js.map +1 -1
  221. package/lib/util/http-client.js +3 -3
  222. package/lib/util/http-client.js.map +1 -1
  223. package/package.json +19 -17
  224. package/readme.md +12 -32
  225. package/src/application-control/config.ts +9 -12
  226. package/src/application-control/environment-variables.test.ts +28 -7
  227. package/src/application-control/environment-variables.ts +42 -59
  228. package/src/application-control/index.ts +3 -16
  229. package/src/application-control/install.ts +39 -13
  230. package/src/application-control/models.ts +6 -87
  231. package/src/application-control/utils.ts +10 -25
  232. package/src/cloud-connection/base-message-handler.ts +118 -0
  233. package/src/cloud-connection/bootstrap-provision.ts +7 -7
  234. package/src/cloud-connection/connection-manager.ts +187 -0
  235. package/src/cloud-connection/device-agent-cloud-connection.ts +130 -723
  236. package/src/cloud-connection/device-agent-message-handler.ts +642 -0
  237. package/src/cloud-connection/device-agent.ts +16 -7
  238. package/src/cloud-connection/live-updates-handler.test.ts +121 -189
  239. package/src/cloud-connection/live-updates-handler.ts +105 -232
  240. package/src/cloud-connection/message-dispatcher.ts +33 -0
  241. package/src/cloud-connection/passthrough-handler.ts +55 -18
  242. package/src/cloud-connection/shadow-handler.test.ts +45 -57
  243. package/src/cloud-connection/shadow-handler.ts +224 -54
  244. package/src/cloud-connection/shadow.ts +4 -1
  245. package/src/cloud-connection/transaction-manager.test.ts +68 -6
  246. package/src/cloud-connection/transaction-manager.ts +69 -41
  247. package/src/device-control/device-control.ts +102 -70
  248. package/src/docker/docker-compose.ts +3 -2
  249. package/src/infrastructure/agent-config.test.ts +6 -2
  250. package/src/infrastructure/agent-config.ts +8 -7
  251. package/src/infrastructure/config-check-utility.test.ts +154 -0
  252. package/src/infrastructure/config-check-utility.ts +77 -0
  253. package/src/infrastructure/device-certificate.test.ts +40 -0
  254. package/src/infrastructure/device-certificate.ts +58 -0
  255. package/src/infrastructure/legacy-migration/legacy-file.test.ts +88 -0
  256. package/src/infrastructure/legacy-migration/legacy-files.ts +101 -0
  257. package/src/infrastructure/legacy-migration/legacy-migration.test.ts +396 -0
  258. package/src/infrastructure/legacy-migration/legacy-migration.ts +229 -0
  259. package/src/infrastructure/require-files-present-ready.test.ts +53 -0
  260. package/src/infrastructure/required-config-checks.ts +33 -0
  261. package/src/infrastructure/tokens-and-device-cfg.ts +12 -10
  262. package/src/jobs/job-handler.ts +146 -0
  263. package/src/local-connection/rabbitmq-connection.ts +22 -17
  264. package/src/secure-tunneling/secure-tunnel-message-handler.ts +56 -0
  265. package/src/secure-tunneling/secure-tunneling.test.ts +20 -22
  266. package/src/secure-tunneling/secure-tunneling.ts +41 -29
  267. package/src/subcommands/app/analytics.ts +2 -4
  268. package/src/subcommands/app/env-vars.ts +72 -9
  269. package/src/subcommands/app/index.ts +3 -11
  270. package/src/subcommands/app/models.ts +5 -81
  271. package/src/subcommands/app/shadow.ts +6 -5
  272. package/src/subcommands/app/version.ts +23 -6
  273. package/src/subcommands/config.ts +42 -0
  274. package/src/subcommands/device/clean.ts +31 -17
  275. package/src/subcommands/device/index.ts +3 -1
  276. package/src/subcommands/device/init.ts +11 -11
  277. package/src/subcommands/device/migrate.ts +20 -0
  278. package/src/subcommands/device/refresh.ts +1 -0
  279. package/src/subcommands/index.ts +3 -1
  280. package/src/util/aai-error.ts +20 -0
  281. package/src/util/{cloud-mode-ready.ts → aws-regions.ts} +0 -24
  282. package/src/util/check-for-updates.ts +14 -30
  283. package/src/util/clean-certs.ts +8 -4
  284. package/src/util/directories.ts +23 -67
  285. package/src/util/file.ts +83 -3
  286. package/src/util/get-device-id.ts +7 -7
  287. package/src/util/http-client.ts +2 -2
  288. package/lib/util/cloud-mode-ready.d.ts +0 -3
  289. package/lib/util/cloud-mode-ready.d.ts.map +0 -1
  290. package/lib/util/cloud-mode-ready.js.map +0 -1
  291. package/lib/util/download-file.d.ts +0 -6
  292. package/lib/util/download-file.d.ts.map +0 -1
  293. package/lib/util/download-file.js +0 -25
  294. package/lib/util/download-file.js.map +0 -1
  295. package/lib/util/fetch-with-timeout.d.ts +0 -4
  296. package/lib/util/fetch-with-timeout.d.ts.map +0 -1
  297. package/lib/util/fetch-with-timeout.js +0 -30
  298. package/lib/util/fetch-with-timeout.js.map +0 -1
  299. package/lib/util/parsing.d.ts +0 -2
  300. package/lib/util/parsing.d.ts.map +0 -1
  301. package/lib/util/parsing.js +0 -17
  302. package/lib/util/parsing.js.map +0 -1
  303. package/src/util/download-file.ts +0 -25
  304. package/src/util/fetch-with-timeout.ts +0 -35
  305. package/src/util/parsing.ts +0 -11
@@ -4,17 +4,13 @@ import { buildApp, getAppDir, requireAppReady } from './utils';
4
4
  import { logger } from '../util/logger';
5
5
  import { AgentConfigFile } from '../infrastructure/agent-config';
6
6
  import { isAppStarted, restartApp } from './status';
7
- import { replaceFalseyWithNull } from '../util/parsing';
7
+ import { EnvVars } from '@alwaysai/device-agent-schemas';
8
8
 
9
- export interface EnvVars {
10
- [service: string]: {
11
- [name: string]: string | null;
12
- };
13
- }
14
-
15
- export async function setEnv(props: { projectId: string; envVars: EnvVars }) {
9
+ export async function setEnv(props: {
10
+ projectId: string;
11
+ envVars: EnvVars;
12
+ }): Promise<void> {
16
13
  const { projectId, envVars } = props;
17
- await requireAppReady({ projectId });
18
14
  const appReleaseHash = await AgentConfigFile().getAppVersion({
19
15
  projectId
20
16
  });
@@ -22,52 +18,7 @@ export async function setEnv(props: { projectId: string; envVars: EnvVars }) {
22
18
  projectId,
23
19
  version: appReleaseHash
24
20
  });
25
-
26
- const composeParsed = await readDockerCompose({ projectId });
27
- if (!('services' in composeParsed)) {
28
- throw new Error(`Docker compose file for ${projectId} has no services!`);
29
- }
30
- const services: string[] = Object.keys(envVars);
31
- for (const s of services) {
32
- if (!Object.keys(composeParsed['services']).includes(s)) {
33
- throw new Error(
34
- `Service ${s} not found in ${JSON.stringify(
35
- composeParsed['services'],
36
- null,
37
- 2
38
- )}`
39
- );
40
- }
41
-
42
- const service = composeParsed['services'][s];
43
- const oldEnv: string[] | undefined = service['environment'];
44
- const envVarsForService = envVars[s];
45
-
46
- const newEnvVarsObj = {};
47
- oldEnv?.forEach((envVarStr: string) => {
48
- const envVarSplit = envVarStr.split('=');
49
- const key = envVarSplit[0];
50
- const value = envVarSplit[1];
51
- if (envVarsForService[key] !== null) {
52
- newEnvVarsObj[key] = value;
53
- }
54
- });
55
-
56
- for (const envVar of Object.keys(envVarsForService)) {
57
- if (envVarsForService[envVar] !== null) {
58
- newEnvVarsObj[envVar] = envVarsForService[envVar];
59
- }
60
- }
61
-
62
- const envVarList: string[] = [];
63
- for (const envVar of Object.keys(newEnvVarsObj)) {
64
- envVarList.push(`${envVar}=${newEnvVarsObj[envVar]}`);
65
- }
66
-
67
- service['environment'] = envVarList;
68
- }
69
-
70
- await writeDockerCompose({ projectId, dockerCompose: composeParsed });
21
+ await setEnvInternal({ projectId, envVars });
71
22
 
72
23
  const appDir = getAppDir(projectId);
73
24
  await buildApp({ appDir });
@@ -89,6 +40,42 @@ export async function setEnv(props: { projectId: string; envVars: EnvVars }) {
89
40
  );
90
41
  }
91
42
 
43
+ export async function setEnvInternal(props: {
44
+ projectId: string;
45
+ envVars: EnvVars;
46
+ }): Promise<void> {
47
+ const { projectId, envVars } = props;
48
+
49
+ const composeParsed = await readDockerCompose({ projectId });
50
+ if (!('services' in composeParsed)) {
51
+ throw new Error(`Docker compose file for ${projectId} has no services!`);
52
+ }
53
+ const services: string[] = Object.keys(envVars);
54
+ const composeServices = composeParsed['services'];
55
+
56
+ services.forEach((s) => {
57
+ if (!Object.keys(composeServices).includes(s)) {
58
+ throw new Error(
59
+ `Service ${s} not found in ${JSON.stringify(composeServices, null, 2)}`
60
+ );
61
+ }
62
+
63
+ const service = composeServices[s];
64
+ const envVarsForService = envVars[s];
65
+
66
+ const envVarList: string[] = Object.keys(envVarsForService).map(
67
+ (envVar) => {
68
+ const value = envVarsForService[envVar];
69
+ return `${envVar}=${value}`;
70
+ }
71
+ );
72
+
73
+ service['environment'] = envVarList;
74
+ });
75
+
76
+ await writeDockerCompose({ projectId, dockerCompose: composeParsed });
77
+ }
78
+
92
79
  export function convertStringEnvsToKeyVal(stringEnvs: string[]) {
93
80
  const envVars = {};
94
81
  stringEnvs.forEach((env: string) => {
@@ -138,9 +125,5 @@ export async function getAllEnvs(props: {
138
125
  }
139
126
  }
140
127
  }
141
-
142
- // Device shadow needs null to delete
143
- replaceFalseyWithNull(envVars, true);
144
-
145
128
  return envVars;
146
129
  }
@@ -9,7 +9,7 @@ import {
9
9
  restartApp
10
10
  } from './status';
11
11
  import { ModelDetails } from './types';
12
- import { EnvVars, getAllEnvs, setEnv } from './environment-variables';
12
+ import { getAllEnvs, setEnv } from './environment-variables';
13
13
 
14
14
  export {
15
15
  readAppCfgFile,
@@ -23,24 +23,11 @@ export {
23
23
  stopApp,
24
24
  restartApp,
25
25
  ModelDetails,
26
- EnvVars,
27
26
  getAllEnvs,
28
27
  setEnv
29
28
  };
30
29
 
31
30
  // CLI-mode only
32
- import {
33
- getAppModels,
34
- removeModel,
35
- replaceModels,
36
- updateModels,
37
- updateModelsWithPresignedUrls
38
- } from './models';
31
+ import { getAppModels, updateModelsWithPresignedUrls } from './models';
39
32
 
40
- export {
41
- getAppModels,
42
- removeModel,
43
- replaceModels,
44
- updateModels,
45
- updateModelsWithPresignedUrls
46
- };
33
+ export { getAppModels, updateModelsWithPresignedUrls };
@@ -2,9 +2,9 @@ import { rimraf } from 'rimraf';
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
4
  import { buildDockerImage } from 'alwaysai/lib/util/docker';
5
- import { JsSpawner, Spawner } from 'alwaysai/lib/util';
6
- import { getAppDir, downloadPackageUsingPresignedUrl, buildApp } from './utils';
7
- import { AppDetails } from '@alwaysai/device-agent-schemas';
5
+ import { JsSpawner, Spawner, stringifyError } from 'alwaysai/lib/util';
6
+ import { getAppDir, buildApp } from './utils';
7
+ import { AppDetails, EnvVars } from '@alwaysai/device-agent-schemas';
8
8
  import { BACKUP_EXT } from './backup';
9
9
  import { startApp, stopApp, isAppStarted } from './status';
10
10
  import { AgentConfigFile } from '../infrastructure/agent-config';
@@ -15,20 +15,23 @@ import {
15
15
  TargetJsonFile,
16
16
  appCleanDocker,
17
17
  getPythonVenvPaths,
18
- installPythonVenv,
18
+ createPythonVenv,
19
19
  installPythonReqs
20
20
  } from 'alwaysai/lib/core/app';
21
21
  import { DOCKER_IMAGE_ID_INITIAL_VALUE } from 'alwaysai/lib/constants';
22
22
  import { runInDir } from '../util/run-in-dir';
23
23
  import { installModelsWithPresignedURLs } from './models';
24
24
  import { logger } from '../util/logger';
25
- import { updateAppCfgFile } from './config';
25
+ import { updateAppCfgFile, writeAppCfgFile } from './config';
26
26
  import {
27
27
  LOCAL_CONNECTION_HOST,
28
28
  LOCAL_CONNECTION_PORT,
29
29
  LOCAL_CONNECTION_ROUTING_KEY
30
30
  } from '../local-connection/constants';
31
31
  import { DOCKERFILE, PYTHON_REQUIREMENTS_FILE_NAME } from 'alwaysai/lib/paths';
32
+ import { downloadToFile } from '../util/file';
33
+ import { setEnvInternal } from './environment-variables';
34
+ import { AppConfig } from '@alwaysai/app-configuration-schemas';
32
35
 
33
36
  type SignedUrlPayloadType = {
34
37
  appInstallPayload: {
@@ -55,8 +58,11 @@ export async function installApp(props: {
55
58
  projectId: string;
56
59
  appReleaseHash: string;
57
60
  signedUrlsPayload: SignedUrlPayloadType;
61
+ appCfg?: AppConfig;
62
+ envVars?: EnvVars;
58
63
  }): Promise<void> {
59
- const { projectId, appReleaseHash, signedUrlsPayload } = props;
64
+ const { projectId, appReleaseHash, signedUrlsPayload, appCfg, envVars } =
65
+ props;
60
66
  logger.info(`Installing ${projectId}:${appReleaseHash}`);
61
67
 
62
68
  const appDir = getAppDir(projectId);
@@ -72,9 +78,11 @@ export async function installApp(props: {
72
78
  logger.info('Application currently running. Stopping...');
73
79
  try {
74
80
  await stopApp({ projectId });
75
- } catch (error) {
81
+ } catch (e) {
76
82
  logger.error(
77
- 'Could not stop the application. Old container might still be present after installation.'
83
+ `Could not stop the application. Old container might still be present after installation.\n${stringifyError(
84
+ e
85
+ )}`
78
86
  );
79
87
  }
80
88
  }
@@ -96,9 +104,9 @@ export async function installApp(props: {
96
104
  // download app package
97
105
  const localDest = path.join(appDir, `${path.basename(appReleaseHash)}.tgz`);
98
106
  const { appSignedUrl } = signedUrlsPayload.appInstallPayload;
99
- await downloadPackageUsingPresignedUrl({
100
- localDest,
101
- presignedUrl: appSignedUrl
107
+ await downloadToFile({
108
+ path: localDest,
109
+ url: appSignedUrl
102
110
  });
103
111
 
104
112
  // Unpack app package and remove tar file
@@ -124,6 +132,22 @@ export async function installApp(props: {
124
132
  path.join(appDir, 'models')
125
133
  );
126
134
 
135
+ // See if there's appCfg or envVars in the transaction, and if so, apply it
136
+
137
+ if (appCfg) {
138
+ logger.debug(`applying appCfg: ${JSON.stringify(appCfg)}`);
139
+ await writeAppCfgFile({ projectId, appCfg });
140
+ }
141
+
142
+ if (envVars) {
143
+ logger.debug(`applying envVarUpdate: ${JSON.stringify(envVars)}`);
144
+ try {
145
+ await setEnvInternal({ projectId, envVars });
146
+ } catch (error) {
147
+ logger.error(`error is : ${error}`);
148
+ }
149
+ }
150
+
127
151
  await installAppBuildReqs({ appDir });
128
152
  await buildApp({ appDir });
129
153
 
@@ -176,7 +200,7 @@ async function installAppBuildReqs(props: { appDir: string }) {
176
200
 
177
201
  const targetSpawner = targetJsonFile.readContainerSpawner();
178
202
  const pythonVenvPaths = await getPythonVenvPaths({ targetCfg });
179
- await installPythonVenv({ targetSpawner, pythonVenvPaths, logger });
203
+ await createPythonVenv({ targetSpawner, pythonVenvPaths, logger });
180
204
  if (await hostSpawner.exists(PYTHON_REQUIREMENTS_FILE_NAME)) {
181
205
  await installPythonReqs({
182
206
  reqFilePath: PYTHON_REQUIREMENTS_FILE_NAME,
@@ -236,7 +260,9 @@ export async function uninstallApp(props: {
236
260
  await stopApp({ projectId });
237
261
  } catch (e) {
238
262
  logger.warn(
239
- `Failed to stop ${projectId}, may be left running...\n${e.message}`
263
+ `Failed to stop ${projectId}, may be left running...\n${stringifyError(
264
+ e
265
+ )}`
240
266
  );
241
267
  }
242
268
  await AgentConfigFile().setAppUninstalled({ projectId });
@@ -1,29 +1,17 @@
1
1
  import { ModelInstallPayload } from '@alwaysai/device-agent-schemas';
2
- import {
3
- appModelsAddComponent,
4
- appModelsRemoveAllComponent,
5
- appModelsRemoveComponent,
6
- appModelsUpdateComponent
7
- } from 'alwaysai/lib/components/app';
8
2
  import { JsSpawner } from 'alwaysai/lib/util';
9
3
  import { logger } from '../util/logger';
10
4
  import { existsSync, createReadStream } from 'fs';
11
5
  import { join, dirname } from 'path';
12
6
  import { AgentConfigFile } from '../infrastructure/agent-config';
13
7
  import { copyDir } from '../util/copy-dir';
14
- import { runInDir } from '../util/run-in-dir';
15
8
  import { isAppStarted, restartApp } from './status';
16
9
  import { ModelDetails } from './types';
17
- import {
18
- buildApp,
19
- downloadPackageUsingPresignedUrl,
20
- getAppDir,
21
- requireAppReady
22
- } from './utils';
10
+ import { buildApp, getAppDir, requireAppReady } from './utils';
23
11
  import { MODEL_JSON_FILE_NAME } from 'alwaysai/lib/core/model';
24
12
  import { writeAppCfgFile } from './config';
25
13
  import { APP_MODELS_DIRECTORY_NAME } from 'alwaysai/lib/paths';
26
- import { pruneDir } from '../util/file';
14
+ import { downloadToFile, pruneDir } from '../util/file';
27
15
  import { AppConfig } from '@alwaysai/app-configuration-schemas';
28
16
 
29
17
  export async function getAppModels(props: { projectId: string }) {
@@ -48,75 +36,6 @@ export async function getAppModels(props: { projectId: string }) {
48
36
  return modelDetails;
49
37
  }
50
38
 
51
- export async function removeModel(props: {
52
- projectId: string;
53
- modelId: string;
54
- }) {
55
- const { projectId, modelId } = props;
56
- await requireAppReady({ projectId });
57
-
58
- const appDir = getAppDir(projectId);
59
-
60
- await runInDir(
61
- appModelsRemoveComponent,
62
- [
63
- {
64
- id: modelId,
65
- purge: true,
66
- removeFromProject: false
67
- }
68
- ],
69
- appDir
70
- );
71
- }
72
-
73
- export async function replaceModels(props: {
74
- projectId: string;
75
- modelIds: string[];
76
- }) {
77
- const { projectId, modelIds } = props;
78
- await requireAppReady({ projectId });
79
-
80
- const appDir = getAppDir(projectId);
81
-
82
- await runInDir(
83
- appModelsRemoveAllComponent,
84
- [
85
- {
86
- purge: true,
87
- removeFromProject: false
88
- }
89
- ],
90
- appDir
91
- );
92
- const modelsAddPromises: Promise<void>[] = [];
93
- for (const modelId of modelIds) {
94
- modelsAddPromises.push(
95
- appModelsAddComponent({
96
- yes: false,
97
- dir: appDir,
98
- id: modelId,
99
- addToProject: false
100
- })
101
- );
102
- }
103
- await Promise.all(modelsAddPromises);
104
-
105
- await buildApp({ appDir });
106
- }
107
-
108
- export async function updateModels(props: { projectId: string }) {
109
- const { projectId } = props;
110
- await requireAppReady({ projectId });
111
-
112
- const appDir = getAppDir(projectId);
113
- await appModelsUpdateComponent({
114
- yes: false,
115
- dir: appDir
116
- });
117
- await buildApp({ appDir });
118
- }
119
-
120
39
  export async function installModelsWithPresignedURLs(
121
40
  modelPayloads: ModelInstallPayload[],
122
41
  targetDir: string
@@ -130,9 +49,9 @@ export async function installModelsWithPresignedURLs(
130
49
  const modelDest = join(targetDir, payload.id);
131
50
  await spawner.mkdirp(modelDest);
132
51
  const localDest = join(modelDest, `${payload.version}.tar.gz`);
133
- await downloadPackageUsingPresignedUrl({
134
- localDest,
135
- presignedUrl: payload.modelSignedUrl
52
+ await downloadToFile({
53
+ path: localDest,
54
+ url: payload.modelSignedUrl
136
55
  });
137
56
  await spawner.untar(createReadStream(localDest), dirname(modelDest));
138
57
  await updateModelJson(modelDest, (modelJson) => ({
@@ -222,7 +141,7 @@ export async function pruneModels(props: {
222
141
  }) {
223
142
  const { projectId, appCfg, path } = props;
224
143
 
225
- const modelsPath = path || join(getAppDir(projectId), 'models');
144
+ const modelsPath = path ?? join(getAppDir(projectId), 'models');
226
145
 
227
146
  if (!existsSync(modelsPath)) {
228
147
  logger.error(
@@ -1,9 +1,7 @@
1
1
  import { compose } from '../docker/docker-compose';
2
2
  import * as path from 'path';
3
- import * as fs from 'fs';
4
3
 
5
4
  import { AgentConfigFile } from '../infrastructure/agent-config';
6
- import { fetchWithTimeout } from '../util/fetch-with-timeout';
7
5
  import {
8
6
  getDockerComposeCmdForApp,
9
7
  TargetJsonFile,
@@ -76,30 +74,17 @@ export async function buildApp(props: { appDir: string }) {
76
74
  `Failed to build application! stdout=${buildOut.out} stderr=${buildOut.err}`
77
75
  );
78
76
  }
79
- }
80
77
 
81
- export async function downloadPackageUsingPresignedUrl(props: {
82
- localDest: string;
83
- presignedUrl: string;
84
- }): Promise<void> {
85
- const { localDest, presignedUrl } = props;
86
- logger.debug(`Downloading package from ${presignedUrl}`);
87
- let response: any;
78
+ // Prune unused Docker resources after a successful build
79
+ logger.debug('Pruning docker system');
88
80
  try {
89
- response = await fetchWithTimeout(presignedUrl);
90
- } catch (e) {
91
- const errorBody = e.type === 'aborted' ? e : await e.response.text();
92
- throw new Error(
93
- `downloadPackageUsingPresignedUrl: Error=${e}\n${errorBody}`
94
- );
81
+ const spawner = JsSpawner();
82
+ const pruneOut = await spawner.run({
83
+ exe: 'docker',
84
+ args: ['system', 'prune', '-f']
85
+ });
86
+ logger.debug(pruneOut);
87
+ } catch (error) {
88
+ logger.error(`Stderr occurred when pruning docker system: ${error}`);
95
89
  }
96
-
97
- /**
98
- * Write the app package to the hash directory
99
- */
100
- const stream = response.body.pipe(fs.createWriteStream(localDest));
101
- await new Promise((resolve, reject) => {
102
- stream.on('finish', resolve);
103
- stream.on('error', reject);
104
- });
105
90
  }
@@ -0,0 +1,118 @@
1
+ import { logger, stringifyError } from 'alwaysai/lib/util';
2
+ import { uninstallApp, rollbackApp } from '../application-control';
3
+ import { createAppBackup } from '../application-control/backup';
4
+ import { AgentConfigFile } from '../infrastructure/agent-config';
5
+ import { SecureTunnelHandlerSingleton } from '../secure-tunneling/secure-tunneling';
6
+ import AaiError from '../util/aai-error';
7
+ import { LiveUpdatesHandler } from './live-updates-handler';
8
+ import { Publisher } from './publisher';
9
+ import { ShadowHandler } from './shadow-handler';
10
+ import {
11
+ TransactionManager,
12
+ ErrorFunction,
13
+ SuccessFunction
14
+ } from './transaction-manager';
15
+
16
+ export interface HandlerContext {
17
+ clientId: string;
18
+ txnMgr: TransactionManager;
19
+ publisher: Publisher;
20
+ shadowHandler: ShadowHandler;
21
+ liveUpdatesHandler: LiveUpdatesHandler;
22
+ secureTunnelHandler: SecureTunnelHandlerSingleton;
23
+ }
24
+
25
+ export abstract class BaseHandler {
26
+ protected clientId: string;
27
+ protected txnMgr: TransactionManager;
28
+ protected publisher: Publisher;
29
+ protected shadowHandler: ShadowHandler;
30
+ protected liveUpdatesHandler: LiveUpdatesHandler;
31
+ protected secureTunnelHandler: SecureTunnelHandlerSingleton;
32
+ protected readonly errorFn?: ErrorFunction;
33
+ protected readonly successFn?: SuccessFunction;
34
+
35
+ constructor(
36
+ context: HandlerContext,
37
+ errorFn?: ErrorFunction,
38
+ successFn?: SuccessFunction
39
+ ) {
40
+ this.clientId = context.clientId;
41
+ this.txnMgr = context.txnMgr;
42
+ this.publisher = context.publisher;
43
+ this.shadowHandler = context.shadowHandler;
44
+ this.liveUpdatesHandler = context.liveUpdatesHandler;
45
+ this.secureTunnelHandler = context.secureTunnelHandler;
46
+ this.errorFn = errorFn;
47
+ this.successFn = successFn;
48
+ }
49
+
50
+ protected async atomicApplicationUninstall(projectId: string) {
51
+ try {
52
+ await uninstallApp({ projectId });
53
+ this.shadowHandler.clearProjectShadow(projectId);
54
+ } catch (e) {
55
+ logger.error(`Failed to uninstall ${projectId}!\n${stringifyError(e)}`);
56
+ throw e;
57
+ }
58
+ }
59
+
60
+ protected async atomicApplicationUpdate<F extends () => any>(
61
+ func: F,
62
+ projectId: string,
63
+ skipUpdateShadow?: boolean
64
+ ): Promise<ReturnType<F>> {
65
+ if (await AgentConfigFile().isAppPresent({ projectId })) {
66
+ // Reject the application update if app is present but not ready
67
+ if (!(await AgentConfigFile().isAppReady({ projectId }))) {
68
+ throw new Error('Application already has installation in progress!');
69
+ }
70
+
71
+ // Try to create a backup, so that there is one available if something goes wrong in the next try:catch.
72
+ try {
73
+ await createAppBackup({ projectId });
74
+ } catch (e) {
75
+ logger.error(
76
+ `Could not create a backup for the project: ${projectId}!\n${stringifyError(
77
+ e
78
+ )}`
79
+ );
80
+ }
81
+ }
82
+
83
+ try {
84
+ const out: ReturnType<F> = await func();
85
+ if (!skipUpdateShadow)
86
+ await this.shadowHandler.updateProjectShadow(projectId);
87
+ return out;
88
+ } catch (errorAppUpdate) {
89
+ logger.error(
90
+ `Failed to update ${projectId}!\n${stringifyError(errorAppUpdate)}`
91
+ );
92
+ // If something goes wrong, first try to rollback
93
+ try {
94
+ await rollbackApp({ projectId });
95
+ } catch (errorRollbackApp) {
96
+ logger.error(
97
+ `Application rollback failed for ${projectId}!\n${stringifyError(
98
+ errorRollbackApp
99
+ )}`
100
+ );
101
+ // and if that fails, uninstall the app as a last resort.
102
+ try {
103
+ await this.atomicApplicationUninstall(projectId);
104
+ } catch {
105
+ // atomicApplicationUninstall logs failure, so there's nothing to do here.
106
+ }
107
+ throw new AaiError(
108
+ 'Application update and rollback failed, uninstalled the application!',
109
+ { cause: errorAppUpdate }
110
+ );
111
+ }
112
+ throw new AaiError(
113
+ 'Application update failed, rolled the application back!',
114
+ { cause: errorAppUpdate }
115
+ );
116
+ }
117
+ }
118
+ }
@@ -1,10 +1,10 @@
1
+ import {
2
+ getBootstrapCertificateFilePath,
3
+ getBootstrapPrivateKeyFilePath
4
+ } from '../infrastructure/device-certificate';
1
5
  import { getIoTCoreEndpointUrl } from '../infrastructure/urls';
2
6
  import { rmBootstrapCertsAndClose } from '../util/clean-certs';
3
- import {
4
- BOOTSTRAP_PRIVATE_KEY_FILE_PATH,
5
- BOOTSTRAP_CERTIFICATE_FILE_PATH,
6
- AWS_ROOT_CERTIFICATE_FILE_PATH
7
- } from '../util/directories';
7
+ import { AWS_ROOT_CERTIFICATE_FILE_PATH } from '../util/directories';
8
8
  import { getDeviceUuid } from '../util/get-device-id';
9
9
  import { logger } from '../util/logger';
10
10
  import { BootstrapAgent } from './device-agent';
@@ -14,8 +14,8 @@ export async function bootstrapProvision() {
14
14
 
15
15
  const clientId = getDeviceUuid();
16
16
  const bootstrapConfig = {
17
- keyPath: BOOTSTRAP_PRIVATE_KEY_FILE_PATH(),
18
- certPath: BOOTSTRAP_CERTIFICATE_FILE_PATH(),
17
+ keyPath: getBootstrapPrivateKeyFilePath(),
18
+ certPath: getBootstrapCertificateFilePath(),
19
19
  caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
20
20
  clientId,
21
21
  host: getIoTCoreEndpointUrl()