@alwaysai/device-agent 0.0.11 → 0.0.13

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 (181) hide show
  1. package/lib/application-control/backup.d.ts.map +1 -1
  2. package/lib/application-control/backup.js +8 -2
  3. package/lib/application-control/backup.js.map +1 -1
  4. package/lib/application-control/config.d.ts +12 -4
  5. package/lib/application-control/config.d.ts.map +1 -1
  6. package/lib/application-control/config.js +59 -16
  7. package/lib/application-control/config.js.map +1 -1
  8. package/lib/application-control/environment-variables.d.ts.map +1 -1
  9. package/lib/application-control/environment-variables.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 +4 -3
  13. package/lib/application-control/index.js.map +1 -1
  14. package/lib/application-control/install.d.ts.map +1 -1
  15. package/lib/application-control/install.js +28 -14
  16. package/lib/application-control/install.js.map +1 -1
  17. package/lib/application-control/models.d.ts +7 -1
  18. package/lib/application-control/models.d.ts.map +1 -1
  19. package/lib/application-control/models.js +69 -39
  20. package/lib/application-control/models.js.map +1 -1
  21. package/lib/application-control/status.d.ts.map +1 -1
  22. package/lib/application-control/status.js +18 -14
  23. package/lib/application-control/status.js.map +1 -1
  24. package/lib/application-control/utils.d.ts +0 -2
  25. package/lib/application-control/utils.d.ts.map +1 -1
  26. package/lib/application-control/utils.js +7 -16
  27. package/lib/application-control/utils.js.map +1 -1
  28. package/lib/cloud-connection/app-install-status.d.ts +16 -0
  29. package/lib/cloud-connection/app-install-status.d.ts.map +1 -0
  30. package/lib/cloud-connection/app-install-status.js +53 -0
  31. package/lib/cloud-connection/app-install-status.js.map +1 -0
  32. package/lib/cloud-connection/bootstrap-provision.d.ts +2 -0
  33. package/lib/cloud-connection/bootstrap-provision.d.ts.map +1 -0
  34. package/lib/cloud-connection/bootstrap-provision.js +34 -0
  35. package/lib/cloud-connection/bootstrap-provision.js.map +1 -0
  36. package/lib/cloud-connection/device-agent-cloud-connection.d.ts +12 -35
  37. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  38. package/lib/cloud-connection/device-agent-cloud-connection.js +170 -387
  39. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  40. package/lib/cloud-connection/device-agent.d.ts.map +1 -1
  41. package/lib/cloud-connection/device-agent.js +22 -26
  42. package/lib/cloud-connection/device-agent.js.map +1 -1
  43. package/lib/cloud-connection/live-updates-handler.d.ts +34 -0
  44. package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -0
  45. package/lib/cloud-connection/live-updates-handler.js +167 -0
  46. package/lib/cloud-connection/live-updates-handler.js.map +1 -0
  47. package/lib/cloud-connection/messages.d.ts +14 -0
  48. package/lib/cloud-connection/messages.d.ts.map +1 -0
  49. package/lib/cloud-connection/messages.js +38 -0
  50. package/lib/cloud-connection/messages.js.map +1 -0
  51. package/lib/cloud-connection/publisher.d.ts +14 -0
  52. package/lib/cloud-connection/publisher.d.ts.map +1 -0
  53. package/lib/cloud-connection/publisher.js +44 -0
  54. package/lib/cloud-connection/publisher.js.map +1 -0
  55. package/lib/cloud-connection/shadow-handler.d.ts +34 -0
  56. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -0
  57. package/lib/cloud-connection/shadow-handler.js +94 -0
  58. package/lib/cloud-connection/shadow-handler.js.map +1 -0
  59. package/lib/cloud-connection/shadow.d.ts +16 -0
  60. package/lib/cloud-connection/shadow.d.ts.map +1 -0
  61. package/lib/cloud-connection/shadow.js +36 -0
  62. package/lib/cloud-connection/shadow.js.map +1 -0
  63. package/lib/device-control/device-control.d.ts.map +1 -1
  64. package/lib/device-control/device-control.js +1 -0
  65. package/lib/device-control/device-control.js.map +1 -1
  66. package/lib/docker/docker-cmd.js +1 -1
  67. package/lib/docker/docker-compose-cmd.d.ts.map +1 -1
  68. package/lib/docker/docker-compose-cmd.js +1 -1
  69. package/lib/docker/docker-compose-cmd.js.map +1 -1
  70. package/lib/endpoints.js +10 -10
  71. package/lib/endpoints.js.map +1 -1
  72. package/lib/environment.d.ts.map +1 -1
  73. package/lib/environment.js.map +1 -1
  74. package/lib/infrastructure/agent-config.d.ts +4 -14
  75. package/lib/infrastructure/agent-config.d.ts.map +1 -1
  76. package/lib/infrastructure/agent-config.js +22 -15
  77. package/lib/infrastructure/agent-config.js.map +1 -1
  78. package/lib/infrastructure/agent-config.test.js +26 -18
  79. package/lib/infrastructure/agent-config.test.js.map +1 -1
  80. package/lib/infrastructure/system-id.d.ts +2 -0
  81. package/lib/infrastructure/system-id.d.ts.map +1 -0
  82. package/lib/infrastructure/system-id.js +21 -0
  83. package/lib/infrastructure/system-id.js.map +1 -0
  84. package/lib/infrastructure/tokens-and-device-cfg.d.ts +4 -0
  85. package/lib/infrastructure/tokens-and-device-cfg.d.ts.map +1 -0
  86. package/lib/infrastructure/tokens-and-device-cfg.js +31 -0
  87. package/lib/infrastructure/tokens-and-device-cfg.js.map +1 -0
  88. package/lib/infrastructure/urls.d.ts.map +1 -1
  89. package/lib/infrastructure/urls.js +3 -3
  90. package/lib/infrastructure/urls.js.map +1 -1
  91. package/lib/root.d.ts.map +1 -1
  92. package/lib/root.js +2 -7
  93. package/lib/root.js.map +1 -1
  94. package/lib/subcommands/app/app.d.ts.map +1 -1
  95. package/lib/subcommands/app/app.js +62 -60
  96. package/lib/subcommands/app/app.js.map +1 -1
  97. package/lib/subcommands/app/index.js +2 -2
  98. package/lib/subcommands/device/clean.d.ts +2 -0
  99. package/lib/subcommands/device/clean.d.ts.map +1 -0
  100. package/lib/subcommands/device/clean.js +29 -0
  101. package/lib/subcommands/device/clean.js.map +1 -0
  102. package/lib/subcommands/device/device.d.ts.map +1 -1
  103. package/lib/subcommands/device/device.js +40 -27
  104. package/lib/subcommands/device/device.js.map +1 -1
  105. package/lib/subcommands/device/index.d.ts.map +1 -1
  106. package/lib/subcommands/device/index.js +2 -1
  107. package/lib/subcommands/device/index.js.map +1 -1
  108. package/lib/subcommands/get-model-package.js +5 -5
  109. package/lib/subcommands/index.js +1 -1
  110. package/lib/subcommands/login.js +8 -8
  111. package/lib/subcommands/login.js.map +1 -1
  112. package/lib/util/clean-certs.d.ts +2 -0
  113. package/lib/util/clean-certs.d.ts.map +1 -0
  114. package/lib/util/clean-certs.js +16 -0
  115. package/lib/util/clean-certs.js.map +1 -0
  116. package/lib/util/directories.d.ts +16 -15
  117. package/lib/util/directories.d.ts.map +1 -1
  118. package/lib/util/directories.js +45 -26
  119. package/lib/util/directories.js.map +1 -1
  120. package/lib/util/get-device-id.d.ts +1 -1
  121. package/lib/util/get-device-id.d.ts.map +1 -1
  122. package/lib/util/get-device-id.js +14 -19
  123. package/lib/util/get-device-id.js.map +1 -1
  124. package/lib/util/http-client.d.ts +1 -1
  125. package/lib/util/http-client.d.ts.map +1 -1
  126. package/lib/util/http-client.js +10 -8
  127. package/lib/util/http-client.js.map +1 -1
  128. package/lib/util/logger.d.ts.map +1 -1
  129. package/lib/util/logger.js +4 -5
  130. package/lib/util/logger.js.map +1 -1
  131. package/lib/util/run-in-dir.d.ts.map +1 -1
  132. package/lib/util/run-in-dir.js +1 -0
  133. package/lib/util/run-in-dir.js.map +1 -1
  134. package/package.json +18 -8
  135. package/src/application-control/backup.ts +8 -3
  136. package/src/application-control/config.ts +75 -13
  137. package/src/application-control/environment-variables.ts +3 -3
  138. package/src/application-control/index.ts +19 -6
  139. package/src/application-control/install.ts +52 -28
  140. package/src/application-control/models.ts +100 -56
  141. package/src/application-control/status.ts +26 -21
  142. package/src/application-control/utils.ts +9 -20
  143. package/src/cloud-connection/app-install-status.ts +62 -0
  144. package/src/cloud-connection/bootstrap-provision.ts +40 -0
  145. package/src/cloud-connection/device-agent-cloud-connection.ts +258 -527
  146. package/src/cloud-connection/device-agent.ts +31 -38
  147. package/src/cloud-connection/live-updates-handler.ts +226 -0
  148. package/src/cloud-connection/messages.ts +39 -0
  149. package/src/cloud-connection/publisher.ts +65 -0
  150. package/src/cloud-connection/shadow-handler.ts +154 -0
  151. package/src/cloud-connection/shadow.ts +50 -0
  152. package/src/device-control/device-control.ts +1 -0
  153. package/src/docker/docker-cmd.ts +1 -1
  154. package/src/docker/docker-compose-cmd.ts +5 -2
  155. package/src/endpoints.ts +9 -9
  156. package/src/environment.ts +8 -3
  157. package/src/infrastructure/agent-config.test.ts +34 -23
  158. package/src/infrastructure/agent-config.ts +33 -20
  159. package/src/infrastructure/system-id.ts +18 -0
  160. package/src/infrastructure/tokens-and-device-cfg.ts +39 -0
  161. package/src/infrastructure/urls.ts +4 -2
  162. package/src/root.ts +2 -8
  163. package/src/subcommands/app/app.ts +64 -62
  164. package/src/subcommands/app/index.ts +3 -3
  165. package/src/subcommands/device/clean.ts +26 -0
  166. package/src/subcommands/device/device.ts +66 -50
  167. package/src/subcommands/device/index.ts +2 -1
  168. package/src/subcommands/get-model-package.ts +5 -5
  169. package/src/subcommands/index.ts +1 -1
  170. package/src/subcommands/login.ts +8 -8
  171. package/src/util/clean-certs.ts +12 -0
  172. package/src/util/directories.ts +68 -52
  173. package/src/util/get-device-id.ts +16 -18
  174. package/src/util/http-client.ts +18 -13
  175. package/src/util/logger.ts +6 -6
  176. package/src/util/run-in-dir.ts +2 -1
  177. package/lib/infrastructure/certificates-and-tokens.d.ts +0 -6
  178. package/lib/infrastructure/certificates-and-tokens.d.ts.map +0 -1
  179. package/lib/infrastructure/certificates-and-tokens.js +0 -43
  180. package/lib/infrastructure/certificates-and-tokens.js.map +0 -1
  181. package/src/infrastructure/certificates-and-tokens.ts +0 -53
@@ -1,10 +1,10 @@
1
- import { keyMirrors, ModelInstallPayload } from '@alwaysai/device-agent-schemas';
2
- import { app } from 'alwaysai/lib/components';
1
+ import * as path from 'path';
2
+ import { ModelInstallPayload } from '@alwaysai/device-agent-schemas';
3
3
  import {
4
4
  appModelsAddComponent,
5
5
  appModelsRemoveAllComponent,
6
6
  appModelsRemoveComponent,
7
- appModelsUpdateComponent,
7
+ appModelsUpdateComponent
8
8
  } from 'alwaysai/lib/components/app';
9
9
  import { JsSpawner } from 'alwaysai/lib/util';
10
10
  import { logger } from '../util/logger';
@@ -13,15 +13,19 @@ import { join, dirname } from 'path';
13
13
  import { AgentConfigFile } from '../infrastructure/agent-config';
14
14
  import { copyDir } from '../util/copy-dir';
15
15
  import { runInDir } from '../util/run-in-dir';
16
- import { getAppStatus, restartApp } from './status';
16
+ import { restartApp } from './status';
17
17
 
18
18
  import { ModelDetails } from './types';
19
19
  import {
20
20
  buildApp,
21
21
  downloadPackageUsingPresignedUrl,
22
22
  getAppDir,
23
- requireAppInstalled,
23
+ requireAppInstalled
24
24
  } from './utils';
25
+ import { MODEL_JSON_FILE_NAME } from 'alwaysai/lib/core/model';
26
+ import { APP_MODELS_DIRECTORY_NAME } from 'alwaysai/lib/constants';
27
+ import { readAppCfgFile, updateAppCfgFile, writeAppCfgFile } from './config';
28
+ import { AppConfig } from '@alwaysai/app-configuration-schemas';
25
29
 
26
30
  export async function getAppModels(props: { projectId: string }) {
27
31
  const { projectId } = props;
@@ -54,12 +58,15 @@ export async function addModel(props: { projectId: string; modelId: string }) {
54
58
  yes: false,
55
59
  dir: appDir,
56
60
  id: modelId,
57
- addToProject: false,
61
+ addToProject: false
58
62
  });
59
63
  await buildApp({ appDir });
60
64
  }
61
65
 
62
- export async function removeModel(props: { projectId: string; modelId: string }) {
66
+ export async function removeModel(props: {
67
+ projectId: string;
68
+ modelId: string;
69
+ }) {
63
70
  const { projectId, modelId } = props;
64
71
  await requireAppInstalled({ projectId });
65
72
 
@@ -71,14 +78,17 @@ export async function removeModel(props: { projectId: string; modelId: string })
71
78
  {
72
79
  id: modelId,
73
80
  purge: true,
74
- removeFromProject: false,
75
- },
81
+ removeFromProject: false
82
+ }
76
83
  ],
77
- appDir,
84
+ appDir
78
85
  );
79
86
  }
80
87
 
81
- export async function replaceModels(props: { projectId: string; modelIds: string[] }) {
88
+ export async function replaceModels(props: {
89
+ projectId: string;
90
+ modelIds: string[];
91
+ }) {
82
92
  const { projectId, modelIds } = props;
83
93
  await requireAppInstalled({ projectId });
84
94
 
@@ -89,17 +99,17 @@ export async function replaceModels(props: { projectId: string; modelIds: string
89
99
  [
90
100
  {
91
101
  purge: true,
92
- removeFromProject: false,
93
- },
102
+ removeFromProject: false
103
+ }
94
104
  ],
95
- appDir,
105
+ appDir
96
106
  );
97
107
  for (const modelId of modelIds) {
98
108
  await appModelsAddComponent({
99
109
  yes: false,
100
110
  dir: appDir,
101
111
  id: modelId,
102
- addToProject: false,
112
+ addToProject: false
103
113
  });
104
114
  }
105
115
  await buildApp({ appDir });
@@ -112,83 +122,117 @@ export async function updateModels(props: { projectId: string }) {
112
122
  const appDir = getAppDir(projectId);
113
123
  await appModelsUpdateComponent({
114
124
  yes: false,
115
- dir: appDir,
125
+ dir: appDir
116
126
  });
117
127
  await buildApp({ appDir });
118
128
  }
119
129
 
120
130
  export async function installModelsWithPresignedURLs(
121
131
  modelPayloads: ModelInstallPayload[],
122
- targetDir: string,
132
+ targetDir: string
123
133
  ) {
124
134
  const spawner = JsSpawner();
135
+ await spawner.mkdirp(targetDir);
125
136
  await Promise.all(
126
137
  modelPayloads.map(async (payload: ModelInstallPayload) => {
138
+ logger.info(`Installing ${payload.id}: ${payload.version}`);
139
+ const version = payload.version;
127
140
  const modelDest = `${targetDir}/${payload.id}`;
128
141
  await spawner.mkdirp(modelDest);
129
142
  const localDest = `${modelDest}/${payload.version}.tar.gz`;
130
143
  await downloadPackageUsingPresignedUrl({
131
144
  localDest,
132
- presignedUrl: payload.modelSignedUrl,
145
+ presignedUrl: payload.modelSignedUrl
133
146
  });
134
147
  await spawner.untar(createReadStream(localDest), dirname(modelDest));
148
+ await updateModelJson(modelDest, (modelJson) => ({
149
+ ...modelJson,
150
+ version
151
+ }));
135
152
  await spawner.rimraf(localDest);
136
- }),
153
+ })
137
154
  );
155
+
156
+ async function readModelJson(dir: string) {
157
+ const filePath = spawner.resolvePath(dir, MODEL_JSON_FILE_NAME);
158
+ const output = await spawner.readFile(filePath);
159
+ const parsed = JSON.parse(output);
160
+ return parsed;
161
+ }
162
+
163
+ async function updateModelJson(dir: string, updater: (current: any) => any) {
164
+ const parsed = await readModelJson(dir);
165
+ const updated = updater(parsed);
166
+ const filePath = spawner.resolvePath(dir, MODEL_JSON_FILE_NAME);
167
+ const serialized = JSON.stringify(updated, null, 2);
168
+ await spawner.writeFile(filePath, serialized);
169
+ }
138
170
  }
139
171
 
140
- export async function updateModelsWithPresignedUrls(
141
- project: string,
142
- modelInstallPayloads: ModelInstallPayload[],
143
- ) {
144
- // create temp dir with all untouched models
172
+ export async function updateModelsWithPresignedUrls(props: {
173
+ projectId: string;
174
+ modelInstallPayloads: ModelInstallPayload[];
175
+ appReleaseHash: string;
176
+ newAppCfg: AppConfig;
177
+ }) {
178
+ const { projectId, modelInstallPayloads, appReleaseHash, newAppCfg } = props;
179
+ logger.info(`Installing models for ${projectId}`);
145
180
  const spawner = JsSpawner();
146
- const appDir = getAppDir(project);
147
- const ogDir = `${appDir}/models`;
148
- const tmpDir = `${ogDir}.tmp`;
181
+ const appDir = getAppDir(projectId);
182
+
183
+ if (await AgentConfigFile().isAppPresent({ projectId })) {
184
+ if (!(await AgentConfigFile().isAppReady({ projectId }))) {
185
+ throw new Error('Application already has installation in progress!');
186
+ }
187
+ logger.info('Updating installed application');
188
+ await AgentConfigFile().setAppInstalling({
189
+ projectId,
190
+ version: appReleaseHash
191
+ });
192
+ } else {
193
+ throw new Error('Application is not installed!');
194
+ }
195
+ const ogAppCfg = await readAppCfgFile({ projectId });
196
+
197
+ const ogDir = path.join(appDir, APP_MODELS_DIRECTORY_NAME);
198
+ // Copy all current models to restore dir in case of failure
149
199
  const restoreDir = `${ogDir}.restore`;
200
+ await spawner.rimraf(restoreDir);
150
201
  await copyDir({ srcPath: ogDir, destPath: restoreDir });
151
- await spawner.rimraf(tmpDir);
152
202
 
153
- try {
154
- // Move unchanged models
155
- const existingModels = await getAppModels({ projectId: project });
156
- for (const model of existingModels) {
157
- if (
158
- !modelInstallPayloads
159
- .map((newModel: ModelInstallPayload) => newModel.id)
160
- .includes(model.modelId)
161
- ) {
162
- // model does not need to be updated
163
- await copyDir({
164
- srcPath: `${ogDir}/${model.modelId}`,
165
- destPath: `${tmpDir}/${model.modelId}`,
166
- });
167
- }
168
- }
203
+ // Create temp dir to install new models
204
+ const tmpDir = `${ogDir}.tmp`;
169
205
 
170
- // install model packages
206
+ try {
207
+ await spawner.rimraf(tmpDir);
208
+ await copyDir({ srcPath: ogDir, destPath: tmpDir });
171
209
  await installModelsWithPresignedURLs(modelInstallPayloads, tmpDir);
210
+ // TODO: Purge outdated models
172
211
  await spawner.rimraf(ogDir);
173
212
  await copyDir({ srcPath: tmpDir, destPath: ogDir });
174
213
  await spawner.rimraf(tmpDir);
175
214
 
176
- await buildApp({ appDir: getAppDir(project) });
215
+ await writeAppCfgFile({ projectId, appCfg: newAppCfg });
216
+ await buildApp({ appDir });
177
217
 
178
- const appStatus = await getAppStatus({ projectId: project });
179
- const appIsRunning =
180
- appStatus.services.length &&
181
- appStatus.services[0].state !== keyMirrors.appState.stopped;
218
+ await AgentConfigFile().setAppInstalled({
219
+ projectId,
220
+ version: appReleaseHash
221
+ });
182
222
 
183
- if (appIsRunning) {
184
- restartApp({ projectId: project });
185
- }
223
+ await restartApp({ projectId });
186
224
 
187
- logger.info(`Models installed for project ${project}`);
225
+ logger.info(`Models installed for project ${projectId}`);
188
226
  } catch (e) {
189
- logger.error(e, 'Error updating app models from presigned URL, restoring models.');
190
- await spawner.rimraf(tmpDir);
227
+ logger.error(
228
+ 'Error updating app models from presigned URL, restoring models.',
229
+ e
230
+ );
191
231
  await spawner.rimraf(ogDir);
192
232
  await copyDir({ srcPath: restoreDir, destPath: ogDir });
233
+ await writeAppCfgFile({ projectId, appCfg: ogAppCfg });
234
+ } finally {
235
+ await spawner.rimraf(tmpDir);
236
+ await spawner.rimraf(restoreDir);
193
237
  }
194
238
  }
@@ -8,7 +8,7 @@ import {
8
8
  ServiceStatusPacket,
9
9
  AppStatePacket,
10
10
  keyMirrors,
11
- AppStateValue,
11
+ AppStateValue
12
12
  } from '@alwaysai/device-agent-schemas';
13
13
  import { AgentConfigFile } from '../infrastructure/agent-config';
14
14
  import { logger } from '../util/logger';
@@ -38,7 +38,7 @@ export async function getAppStatus(props: {
38
38
 
39
39
  const appDetails = {
40
40
  projectId,
41
- version: await AgentConfigFile().getAppVersion({ projectId }),
41
+ version: await AgentConfigFile().getAppVersion({ projectId })
42
42
  };
43
43
  if (!(await AgentConfigFile().isAppReady({ projectId }))) {
44
44
  // App is being installed or updated
@@ -53,7 +53,7 @@ export async function getAppStatus(props: {
53
53
 
54
54
  if (status.exitCode !== 0) {
55
55
  throw new Error(
56
- `Failed to get application status! stdout=${status.out} stderr=${status.err}`,
56
+ `Failed to get application status! stdout=${status.out} stderr=${status.err}`
57
57
  );
58
58
  }
59
59
 
@@ -64,7 +64,7 @@ export async function getAppStatus(props: {
64
64
  const containerId = await spawner.run({
65
65
  exe: 'docker-compose',
66
66
  cwd: appDir,
67
- args: ['ps', '-q', name],
67
+ args: ['ps', '-q', name]
68
68
  });
69
69
  if (containerId === '') {
70
70
  // The service was not yet started or failed to start
@@ -72,7 +72,7 @@ export async function getAppStatus(props: {
72
72
  } else {
73
73
  const slashContainerName = await spawner.run({
74
74
  exe: 'docker',
75
- args: ['inspect', '-f', '{{.Name}}', containerId],
75
+ args: ['inspect', '-f', '{{.Name}}', containerId]
76
76
  });
77
77
  const containerName = slashContainerName.substring(1);
78
78
 
@@ -102,8 +102,8 @@ export async function getAppStatus(props: {
102
102
  `Unable to find container for ${name} in ${JSON.stringify(
103
103
  status.data.services,
104
104
  null,
105
- 2,
106
- )}`,
105
+ 2
106
+ )}`
107
107
  );
108
108
  }
109
109
  }
@@ -134,7 +134,7 @@ export async function getAppLogs(props: {
134
134
  return await JsSpawner().runStreaming({
135
135
  exe: 'docker-compose',
136
136
  args: ['logs', '-f', ...argsList, ...serviceList],
137
- cwd: appDir,
137
+ cwd: appDir
138
138
  });
139
139
  }
140
140
 
@@ -148,19 +148,19 @@ export async function startApp(props: {
148
148
  const appDir = getAppDir(projectId);
149
149
  if (dockerLoginToken !== undefined) {
150
150
  const result = await runDockerLogin({ token: dockerLoginToken });
151
- logger.info(result);
151
+ logger.debug(`docker login: ${result}`);
152
152
  }
153
153
 
154
154
  // TODO: Check if app is running
155
155
  // Start app
156
156
  const upOut = await compose.upAll({ cwd: appDir });
157
- logger.info(upOut);
157
+ logger.debug(`docker-compose up: ${JSON.stringify(upOut, null, 2)}`);
158
158
  if (upOut.exitCode !== 0) {
159
159
  throw new Error(
160
- `Failed to start application! stdout=${upOut.out} stderr=${upOut.err}`,
160
+ `Failed to start application! stdout=${upOut.out} stderr=${upOut.err}`
161
161
  );
162
162
  }
163
- logger.info('Started', projectId);
163
+ logger.info(`Started ${projectId}`);
164
164
  }
165
165
 
166
166
  export async function stopApp(props: { projectId: string }): Promise<void> {
@@ -168,16 +168,21 @@ export async function stopApp(props: { projectId: string }): Promise<void> {
168
168
  await requireAppInstalled({ projectId });
169
169
 
170
170
  const appDir = getAppDir(projectId);
171
- // TODO: Check if app is running
172
- // Stop app
173
- const output = await compose.down({ cwd: appDir });
174
- logger.info(output);
175
- if (output.exitCode !== 0) {
176
- throw new Error(
177
- `Failed to stop application! stdout=${output.out} stderr=${output.err}`,
178
- );
171
+ const appStatus = await getAppStatus({ projectId });
172
+ const appIsRunning =
173
+ appStatus.services.length &&
174
+ appStatus.services[0].state !== keyMirrors.appState.stopped;
175
+ if (appIsRunning) {
176
+ // Stop app
177
+ const output = await compose.down({ cwd: appDir });
178
+ logger.debug(`docker-compose down: ${JSON.stringify(output, null, 2)}`);
179
+ if (output.exitCode !== 0) {
180
+ throw new Error(
181
+ `Failed to stop application! stdout=${output.out} stderr=${output.err}`
182
+ );
183
+ }
184
+ logger.info(`Stopped ${projectId}`);
179
185
  }
180
- logger.info('Stopped', projectId);
181
186
  }
182
187
 
183
188
  export async function restartApp(props: { projectId: string }): Promise<void> {
@@ -1,17 +1,15 @@
1
1
  import compose from 'docker-compose';
2
2
  import * as path from 'path';
3
3
  import * as fs from 'fs';
4
- import { AAI_DIR, TARGET_JSON_FILE_NAME } from 'alwaysai/lib/constants';
4
+ import { TARGET_JSON_FILE_NAME } from 'alwaysai/lib/constants';
5
5
 
6
6
  import { AgentConfigFile } from '../infrastructure/agent-config';
7
7
  import nodeFetch from 'node-fetch';
8
8
  import { TargetJsonFile } from 'alwaysai/lib/core/app';
9
9
  import { appDeployLinuxAndRemoteDevice } from 'alwaysai/lib/components/app';
10
10
  import { runInDir } from '../util/run-in-dir';
11
- import { JsSpawner } from 'alwaysai/lib/util';
12
11
  import { logger } from '../util/logger';
13
-
14
- export const APP_ROOT = path.join(AAI_DIR, 'applications');
12
+ import { APP_ROOT } from '../util/directories';
15
13
 
16
14
  export function getAppDir(projectId: string): string {
17
15
  return path.join(APP_ROOT, projectId);
@@ -46,17 +44,17 @@ export async function buildApp(props: { appDir: string }) {
46
44
  logs: false,
47
45
  stop: false,
48
46
  targetJson,
49
- targetConfig,
50
- },
47
+ targetConfig
48
+ }
51
49
  ],
52
- appDir,
50
+ appDir
53
51
  );
54
52
 
55
53
  const buildOut = await compose.buildAll({ cwd: appDir });
56
- logger.info(buildOut);
54
+ logger.debug(`docker-compose build: ${JSON.stringify(buildOut, null, 2)}`);
57
55
  if (buildOut.exitCode !== 0) {
58
56
  throw new Error(
59
- `Failed to build application! stdout=${buildOut.out} stderr=${buildOut.err}`,
57
+ `Failed to build application! stdout=${buildOut.out} stderr=${buildOut.err}`
60
58
  );
61
59
  }
62
60
  }
@@ -65,13 +63,13 @@ export async function downloadPackageUsingPresignedUrl(props: {
65
63
  localDest: string;
66
64
  presignedUrl: string;
67
65
  }): Promise<void> {
68
- logger.info('downloading URL');
69
66
  const { localDest, presignedUrl } = props;
67
+ logger.info(`Downloading app package from ${presignedUrl}`);
70
68
  const response = await nodeFetch(presignedUrl);
71
69
  if (response.status !== 200) {
72
70
  // If the URL is invalid; I think we shouldn't get here with the new changes
73
71
  throw new Error(
74
- `Status Code: ${response.status}, ${response.statusText}. Response: ${response.body}`,
72
+ `Status Code: ${response.status}, ${response.statusText}. Response: ${response.body}`
75
73
  );
76
74
  }
77
75
 
@@ -84,12 +82,3 @@ export async function downloadPackageUsingPresignedUrl(props: {
84
82
  stream.on('error', reject);
85
83
  });
86
84
  }
87
-
88
- export async function getAppConfig(project: string) {
89
- const appCfgPath = path.join(getAppDir(project), 'alwaysai.app.json');
90
- if (!fs.existsSync(appCfgPath)) {
91
- throw new Error(`Application config not found for project ${project}`);
92
- }
93
-
94
- return JSON.parse(await JsSpawner().readFile(appCfgPath));
95
- }
@@ -0,0 +1,62 @@
1
+ import { AppInstallStatusPacket } from '@alwaysai/device-agent-schemas';
2
+ import { AppInstallStatusValue } from '@alwaysai/device-agent-schemas/lib/constants';
3
+
4
+ export class AppInstallStatus {
5
+ private appReleaseHash: string;
6
+ private status: AppInstallStatusValue;
7
+ private message?: string;
8
+
9
+ constructor(appReleaseHash, status, message?) {
10
+ this.appReleaseHash = appReleaseHash;
11
+ this.status = status;
12
+ this.message = message;
13
+ }
14
+
15
+ public getAppReleaseHash() {
16
+ return this.appReleaseHash;
17
+ }
18
+
19
+ public update(status, message?) {
20
+ this.status = status;
21
+ this.message = message;
22
+ }
23
+
24
+ public getStatusPacket(): AppInstallStatusPacket {
25
+ return {
26
+ status: this.status,
27
+ message: this.message,
28
+ appReleaseHash: this.appReleaseHash
29
+ };
30
+ }
31
+ }
32
+
33
+ export class AppInstallStatusManager {
34
+ private appList: AppInstallStatus[] = [];
35
+
36
+ public update(appReleaseHash, status, message?) {
37
+ // Update status if existing
38
+ // TODO: Reimplement this as a map
39
+ for (const app of this.appList) {
40
+ if (app.getAppReleaseHash() === appReleaseHash) {
41
+ app.update(status, message);
42
+ return;
43
+ }
44
+ }
45
+ // App was not found, so add to list
46
+ const appInstallStatus = new AppInstallStatus(
47
+ appReleaseHash,
48
+ status,
49
+ message
50
+ );
51
+ this.appList.push(appInstallStatus);
52
+ }
53
+
54
+ public getStatusPacket(appReleaseHash) {
55
+ for (const app of this.appList) {
56
+ if (app.getAppReleaseHash() === appReleaseHash) {
57
+ return app.getStatusPacket();
58
+ }
59
+ }
60
+ throw new Error(`No status for ${appReleaseHash}`);
61
+ }
62
+ }
@@ -0,0 +1,40 @@
1
+ import { logger } from 'alwaysai/lib/util';
2
+ import { getIoTCoreEndpointUrl } from '../infrastructure/urls';
3
+ import { rmBootstrapCertsAndClose } from '../util/clean-certs';
4
+ import {
5
+ BOOTSTRAP_DEVICE_PRIVATE_KEY_FILE_PATH,
6
+ BOOTSTRAP_DEVICE_CERTIFICATE_FILE_PATH,
7
+ AWS_ROOT_CERTIFICATE_FILE_PATH
8
+ } from '../util/directories';
9
+ import { getDeviceUuid } from '../util/get-device-id';
10
+ import { BootstrapAgent } from './device-agent';
11
+
12
+ export function bootstrapProvision() {
13
+ setTimeout(rmBootstrapCertsAndClose, 60000);
14
+
15
+ const clientId = getDeviceUuid();
16
+ const bootstrapConfig = {
17
+ keyPath: BOOTSTRAP_DEVICE_PRIVATE_KEY_FILE_PATH(),
18
+ certPath: BOOTSTRAP_DEVICE_CERTIFICATE_FILE_PATH(),
19
+ caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
20
+ clientId,
21
+ host: getIoTCoreEndpointUrl()
22
+ };
23
+
24
+ const bootstrapAgent = new BootstrapAgent(bootstrapConfig);
25
+ bootstrapAgent.subscribeToAllTopics();
26
+
27
+ bootstrapAgent.publishMessage('$aws/certificates/create/json', '');
28
+
29
+ bootstrapAgent.device.on('connect', () => {
30
+ logger.info('Your device is being provisioned');
31
+ });
32
+
33
+ bootstrapAgent.device.on('message', (topic: string, payload: string) => {
34
+ bootstrapAgent.handleAwsCertificateTopics(topic, payload);
35
+ });
36
+
37
+ bootstrapAgent.device.on('packetsend', (packet: any) => {
38
+ logger.debug(`Sending packet: ${JSON.stringify({ packet }, null, 2)}`);
39
+ });
40
+ }