@alwaysai/device-agent 1.3.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/application-control/config.js +2 -2
- package/lib/application-control/config.js.map +1 -1
- package/lib/application-control/environment-variables.d.ts.map +1 -1
- package/lib/application-control/environment-variables.js +9 -4
- package/lib/application-control/environment-variables.js.map +1 -1
- package/lib/application-control/environment-variables.test.js +1 -1
- package/lib/application-control/environment-variables.test.js.map +1 -1
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +7 -2
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/models.d.ts +5 -0
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +28 -14
- package/lib/application-control/models.js.map +1 -1
- package/lib/application-control/status.d.ts.map +1 -1
- package/lib/application-control/status.js +14 -17
- package/lib/application-control/status.js.map +1 -1
- package/lib/application-control/utils.js +2 -2
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +5 -5
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +140 -105
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts +4 -2
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +46 -25
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.test.js +132 -16
- package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
- package/lib/cloud-connection/messages.d.ts.map +1 -1
- package/lib/cloud-connection/messages.js +3 -4
- package/lib/cloud-connection/messages.js.map +1 -1
- package/lib/cloud-connection/passthrough-handler.d.ts +5 -3
- package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
- package/lib/cloud-connection/passthrough-handler.js +76 -62
- package/lib/cloud-connection/passthrough-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.d.ts +16 -21
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +162 -108
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.test.js +100 -83
- package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.d.ts +3 -0
- package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
- package/lib/cloud-connection/transaction-manager.js +11 -0
- package/lib/cloud-connection/transaction-manager.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.test.js +102 -0
- package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
- package/lib/device-control/device-control.d.ts +16 -15
- package/lib/device-control/device-control.d.ts.map +1 -1
- package/lib/device-control/device-control.js +117 -18
- package/lib/device-control/device-control.js.map +1 -1
- package/lib/docker/docker-compose.d.ts +14 -0
- package/lib/docker/docker-compose.d.ts.map +1 -0
- package/lib/docker/docker-compose.js +56 -0
- package/lib/docker/docker-compose.js.map +1 -0
- package/lib/index.js +2 -5
- package/lib/index.js.map +1 -1
- package/lib/infrastructure/agent-config.d.ts +45 -14
- package/lib/infrastructure/agent-config.d.ts.map +1 -1
- package/lib/infrastructure/agent-config.js +30 -15
- package/lib/infrastructure/agent-config.js.map +1 -1
- package/lib/infrastructure/agent-config.test.js +3 -0
- package/lib/infrastructure/agent-config.test.js.map +1 -1
- package/lib/local-connection/rabbitmq-connection.js +11 -11
- package/lib/local-connection/rabbitmq-connection.js.map +1 -1
- package/lib/secure-tunneling/secure-tunneling.d.ts +97 -0
- package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -0
- package/lib/secure-tunneling/secure-tunneling.js +435 -0
- package/lib/secure-tunneling/secure-tunneling.js.map +1 -0
- package/lib/secure-tunneling/secure-tunneling.test.d.ts +2 -0
- package/lib/secure-tunneling/secure-tunneling.test.d.ts.map +1 -0
- package/lib/secure-tunneling/secure-tunneling.test.js +1070 -0
- package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -0
- package/lib/secure-tunneling/spawner-detached.d.ts +6 -0
- package/lib/secure-tunneling/spawner-detached.d.ts.map +1 -0
- package/lib/secure-tunneling/spawner-detached.js +107 -0
- package/lib/secure-tunneling/spawner-detached.js.map +1 -0
- package/lib/subcommands/app/analytics.d.ts.map +1 -1
- package/lib/subcommands/app/analytics.js +9 -13
- package/lib/subcommands/app/analytics.js.map +1 -1
- package/lib/subcommands/app/env-vars.d.ts.map +1 -1
- package/lib/subcommands/app/env-vars.js +11 -16
- package/lib/subcommands/app/env-vars.js.map +1 -1
- package/lib/subcommands/app/models.d.ts.map +1 -1
- package/lib/subcommands/app/models.js +12 -16
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/device/clean.d.ts.map +1 -1
- package/lib/subcommands/device/clean.js +8 -6
- package/lib/subcommands/device/clean.js.map +1 -1
- package/lib/subcommands/device/get-info.d.ts +2 -0
- package/lib/subcommands/device/get-info.d.ts.map +1 -0
- package/lib/subcommands/device/get-info.js +36 -0
- package/lib/subcommands/device/get-info.js.map +1 -0
- package/lib/subcommands/device/index.d.ts.map +1 -1
- package/lib/subcommands/device/index.js +11 -2
- package/lib/subcommands/device/index.js.map +1 -1
- package/lib/subcommands/device/init.d.ts +5 -0
- package/lib/subcommands/device/init.d.ts.map +1 -0
- package/lib/subcommands/device/{device.js → init.js} +5 -36
- package/lib/subcommands/device/init.js.map +1 -0
- package/lib/subcommands/device/refresh.d.ts +2 -0
- package/lib/subcommands/device/refresh.d.ts.map +1 -0
- package/lib/subcommands/device/refresh.js +24 -0
- package/lib/subcommands/device/refresh.js.map +1 -0
- package/lib/subcommands/device/restart.d.ts +2 -0
- package/lib/subcommands/device/restart.d.ts.map +1 -0
- package/lib/subcommands/device/restart.js +14 -0
- package/lib/subcommands/device/restart.js.map +1 -0
- package/lib/util/check-for-updates.d.ts +3 -0
- package/lib/util/check-for-updates.d.ts.map +1 -0
- package/lib/util/check-for-updates.js +69 -0
- package/lib/util/check-for-updates.js.map +1 -0
- package/lib/util/cloud-mode-ready.d.ts +1 -0
- package/lib/util/cloud-mode-ready.d.ts.map +1 -1
- package/lib/util/cloud-mode-ready.js +36 -1
- package/lib/util/cloud-mode-ready.js.map +1 -1
- package/lib/util/file.d.ts +7 -0
- package/lib/util/file.d.ts.map +1 -0
- package/lib/util/file.js +66 -0
- package/lib/util/file.js.map +1 -0
- package/lib/util/file.test.d.ts +2 -0
- package/lib/util/file.test.d.ts.map +1 -0
- package/lib/util/file.test.js +87 -0
- package/lib/util/file.test.js.map +1 -0
- package/package.json +8 -7
- package/readme.md +3 -3
- package/src/application-control/config.ts +1 -1
- package/src/application-control/environment-variables.test.ts +1 -1
- package/src/application-control/environment-variables.ts +9 -6
- package/src/application-control/install.ts +8 -3
- package/src/application-control/models.ts +47 -19
- package/src/application-control/status.ts +16 -14
- package/src/application-control/utils.ts +1 -1
- package/src/cloud-connection/device-agent-cloud-connection.ts +202 -148
- package/src/cloud-connection/live-updates-handler.test.ts +161 -20
- package/src/cloud-connection/live-updates-handler.ts +63 -31
- package/src/cloud-connection/messages.ts +3 -4
- package/src/cloud-connection/passthrough-handler.ts +98 -76
- package/src/cloud-connection/shadow-handler.test.ts +101 -84
- package/src/cloud-connection/shadow-handler.ts +287 -133
- package/src/cloud-connection/transaction-manager.test.ts +124 -0
- package/src/cloud-connection/transaction-manager.ts +15 -0
- package/src/device-control/device-control.ts +125 -23
- package/src/docker/docker-compose.ts +60 -0
- package/src/index.ts +2 -6
- package/src/infrastructure/agent-config.test.ts +3 -0
- package/src/infrastructure/agent-config.ts +38 -40
- package/src/local-connection/rabbitmq-connection.ts +8 -8
- package/src/secure-tunneling/secure-tunneling.test.ts +1239 -0
- package/src/secure-tunneling/secure-tunneling.ts +599 -0
- package/src/secure-tunneling/spawner-detached.ts +123 -0
- package/src/subcommands/app/analytics.ts +16 -13
- package/src/subcommands/app/env-vars.ts +18 -16
- package/src/subcommands/app/models.ts +20 -16
- package/src/subcommands/device/clean.ts +5 -2
- package/src/subcommands/device/get-info.ts +49 -0
- package/src/subcommands/device/index.ts +11 -2
- package/src/subcommands/device/{device.ts → init.ts} +5 -47
- package/src/subcommands/device/refresh.ts +22 -0
- package/src/subcommands/device/restart.ts +11 -0
- package/src/util/check-for-updates.ts +69 -0
- package/src/util/cloud-mode-ready.ts +36 -0
- package/src/util/file.test.ts +90 -0
- package/src/util/file.ts +76 -0
- package/lib/docker/docker-compose-cmd.d.ts +0 -5
- package/lib/docker/docker-compose-cmd.d.ts.map +0 -1
- package/lib/docker/docker-compose-cmd.js +0 -16
- package/lib/docker/docker-compose-cmd.js.map +0 -1
- package/lib/secure-tunneling/index.d.ts +0 -5
- package/lib/secure-tunneling/index.d.ts.map +0 -1
- package/lib/secure-tunneling/index.js +0 -64
- package/lib/secure-tunneling/index.js.map +0 -1
- package/lib/subcommands/device/device.d.ts +0 -7
- package/lib/subcommands/device/device.d.ts.map +0 -1
- package/lib/subcommands/device/device.js.map +0 -1
- package/lib/util/safe-rimraf.d.ts +0 -2
- package/lib/util/safe-rimraf.d.ts.map +0 -1
- package/lib/util/safe-rimraf.js +0 -16
- package/lib/util/safe-rimraf.js.map +0 -1
- package/src/docker/docker-compose-cmd.ts +0 -15
- package/src/secure-tunneling/index.ts +0 -74
- package/src/util/safe-rimraf.ts +0 -14
|
@@ -1,59 +1,60 @@
|
|
|
1
1
|
// eslint-disable-next-line
|
|
2
|
-
const awsIot = require(
|
|
3
|
-
import { getIoTCoreEndpointUrl } from '../infrastructure/urls';
|
|
4
|
-
import { existsSync } from 'fs';
|
|
5
|
-
import {
|
|
6
|
-
BOOTSTRAP_PRIVATE_KEY_FILE_PATH,
|
|
7
|
-
AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
8
|
-
BOOTSTRAP_CERTIFICATES_DIR_PATH,
|
|
9
|
-
DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
10
|
-
DEVICE_CERTIFICATE_FILE_PATH
|
|
11
|
-
} from '../util/directories';
|
|
2
|
+
const awsIot = require('aws-iot-device-sdk');
|
|
12
3
|
import {
|
|
13
|
-
keyMirrors,
|
|
14
|
-
SignedUrlsRequestPayload,
|
|
15
|
-
getToDeviceTopic,
|
|
16
4
|
AppInstallResponsePayload,
|
|
17
|
-
validateToDeviceAgentMessage,
|
|
18
|
-
ToDeviceAgentMessage,
|
|
19
|
-
ToCloudMessage,
|
|
20
5
|
AppStateControlPayload,
|
|
21
6
|
AppVersionControlInstallPayload,
|
|
22
7
|
AppVersionControlUninstallPayload,
|
|
23
8
|
DeviceActionPayload,
|
|
9
|
+
ModelsInstallResponsePayload,
|
|
10
|
+
SignedUrlsRequestPayload,
|
|
11
|
+
ToCloudMessage,
|
|
12
|
+
ToDeviceAgentMessage,
|
|
13
|
+
getToDeviceTopic,
|
|
24
14
|
buildSignedUrlsRequestMessage,
|
|
25
15
|
buildToClientStatusResponseMessage,
|
|
26
|
-
StatusResponsePayload
|
|
16
|
+
StatusResponsePayload,
|
|
17
|
+
keyMirrors,
|
|
18
|
+
validateToDeviceAgentMessage,
|
|
19
|
+
validateSecureTunnelShadowUpdate
|
|
27
20
|
} from '@alwaysai/device-agent-schemas';
|
|
28
|
-
import {
|
|
29
|
-
import { logger } from '../util/logger';
|
|
30
|
-
import { cloudModeReady } from '../util/cloud-mode-ready';
|
|
31
|
-
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
21
|
+
import { existsSync } from 'fs';
|
|
32
22
|
import {
|
|
23
|
+
installApp,
|
|
24
|
+
restartApp,
|
|
33
25
|
startApp,
|
|
34
26
|
stopApp,
|
|
35
|
-
restartApp,
|
|
36
|
-
updateModelsWithPresignedUrls,
|
|
37
|
-
installApp,
|
|
38
27
|
uninstallApp,
|
|
39
28
|
updateAppCfg,
|
|
40
|
-
|
|
29
|
+
updateModelsWithPresignedUrls
|
|
41
30
|
} from '../application-control';
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import { Publisher } from './publisher';
|
|
45
|
-
import { bootstrapProvision } from './bootstrap-provision';
|
|
46
|
-
import { PassthroughHandler, runChannel } from './passthrough-handler';
|
|
31
|
+
import { createAppBackup, rollbackApp } from '../application-control/backup';
|
|
32
|
+
import { reboot } from '../device-control/device-control';
|
|
47
33
|
import { ALWAYSAI_ANALYTICS_PASSTHROUGH } from '../environment';
|
|
48
|
-
import {
|
|
34
|
+
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
35
|
+
import { getIoTCoreEndpointUrl } from '../infrastructure/urls';
|
|
36
|
+
import { SecureTunnelHandlerSingleton } from '../secure-tunneling/secure-tunneling';
|
|
37
|
+
import { cloudModeReady } from '../util/cloud-mode-ready';
|
|
38
|
+
import {
|
|
39
|
+
AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
40
|
+
BOOTSTRAP_CERTIFICATES_DIR_PATH,
|
|
41
|
+
BOOTSTRAP_PRIVATE_KEY_FILE_PATH,
|
|
42
|
+
DEVICE_CERTIFICATE_FILE_PATH,
|
|
43
|
+
DEVICE_PRIVATE_KEY_FILE_PATH
|
|
44
|
+
} from '../util/directories';
|
|
45
|
+
import { getDeviceUuid } from '../util/get-device-id';
|
|
46
|
+
import { logger } from '../util/logger';
|
|
49
47
|
import sleep from '../util/sleep';
|
|
50
|
-
import {
|
|
48
|
+
import { bootstrapProvision } from './bootstrap-provision';
|
|
49
|
+
import { LiveUpdatesHandler } from './live-updates-handler';
|
|
50
|
+
import { PassthroughHandler } from './passthrough-handler';
|
|
51
|
+
import { Publisher } from './publisher';
|
|
52
|
+
import { ShadowHandler, ShadowUpdate } from './shadow-handler';
|
|
51
53
|
import { TransactionManager } from './transaction-manager';
|
|
52
|
-
import { reboot } from '../device-control/device-control';
|
|
53
|
-
|
|
54
54
|
import { exec } from 'child_process';
|
|
55
55
|
import { promisify } from 'util';
|
|
56
|
-
import {
|
|
56
|
+
import { pruneModels } from '../application-control/models';
|
|
57
|
+
import { getDeviceAgentVersion } from '../util/check-for-updates';
|
|
57
58
|
|
|
58
59
|
const exec_promise = promisify(exec);
|
|
59
60
|
|
|
@@ -69,8 +70,8 @@ export class DeviceAgentCloudConnection {
|
|
|
69
70
|
private port = 8883;
|
|
70
71
|
private readonly toDeviceTopic = getToDeviceTopic(this.clientId);
|
|
71
72
|
private readonly secureTunnelNotifyTopic = `$aws/things/${this.clientId}/tunnels/notify`;
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
private readonly secureTunnelHandler =
|
|
74
|
+
SecureTunnelHandlerSingleton.getInstance();
|
|
74
75
|
|
|
75
76
|
private handleAppStateControl = async (
|
|
76
77
|
payload: AppStateControlPayload
|
|
@@ -144,46 +145,50 @@ export class DeviceAgentCloudConnection {
|
|
|
144
145
|
appInstallPayload,
|
|
145
146
|
modelsInstallPayload
|
|
146
147
|
};
|
|
147
|
-
await this.atomicApplicationUpdate(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
);
|
|
148
|
+
await this.atomicApplicationUpdate(async () => {
|
|
149
|
+
this.shadowHandler.clearProjectShadow(projectId);
|
|
150
|
+
await installApp({ projectId, appReleaseHash, signedUrlsPayload });
|
|
151
|
+
}, projectId);
|
|
152
152
|
return true;
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
private handleModelsInstallCloudResponsePayload = async (
|
|
156
|
-
payload: ModelsInstallResponsePayload
|
|
156
|
+
payload: ModelsInstallResponsePayload,
|
|
157
|
+
txId: string
|
|
157
158
|
): Promise<boolean> => {
|
|
158
|
-
const
|
|
159
|
+
const projectId = payload.modelsInstallResponse.projectId;
|
|
160
|
+
|
|
161
|
+
const update = this.txnMgr.getAppCfgUpdateFromTxID(txId);
|
|
159
162
|
if (update === undefined) {
|
|
160
163
|
throw new Error(
|
|
161
164
|
'Unknown error while updating models via application config! No config present for model update.'
|
|
162
165
|
);
|
|
163
166
|
}
|
|
164
167
|
const { appCfgUpdate, envVarUpdate } = update;
|
|
165
|
-
const projectId = payload.modelsInstallResponse.projectId;
|
|
166
168
|
if (appCfgUpdate) {
|
|
167
169
|
await this.atomicApplicationUpdate(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
{
|
|
170
|
+
async () =>
|
|
171
|
+
await updateModelsWithPresignedUrls({
|
|
171
172
|
projectId,
|
|
172
173
|
modelInstallPayloads: payload.modelsInstallResponse.newModels,
|
|
173
174
|
newAppCfg: appCfgUpdate.newAppCfg
|
|
174
|
-
}
|
|
175
|
-
],
|
|
175
|
+
}),
|
|
176
176
|
projectId
|
|
177
177
|
);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
if (envVarUpdate) {
|
|
181
181
|
await this.atomicApplicationUpdate(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
async () =>
|
|
183
|
+
await this.shadowHandler.updateProjectEnvVars({
|
|
184
|
+
projectId,
|
|
185
|
+
envVars: envVarUpdate.envVars
|
|
186
|
+
}),
|
|
187
|
+
projectId,
|
|
188
|
+
true
|
|
185
189
|
);
|
|
186
190
|
}
|
|
191
|
+
|
|
187
192
|
return true;
|
|
188
193
|
};
|
|
189
194
|
|
|
@@ -214,7 +219,7 @@ export class DeviceAgentCloudConnection {
|
|
|
214
219
|
private async atomicApplicationUninstall(projectId: string) {
|
|
215
220
|
try {
|
|
216
221
|
await uninstallApp({ projectId });
|
|
217
|
-
this.shadowHandler.
|
|
222
|
+
this.shadowHandler.clearProjectShadow(projectId);
|
|
218
223
|
} catch (e) {
|
|
219
224
|
logger.error(`Failed to uninstall ${projectId}: ${e.message}`);
|
|
220
225
|
throw e;
|
|
@@ -222,11 +227,11 @@ export class DeviceAgentCloudConnection {
|
|
|
222
227
|
}
|
|
223
228
|
|
|
224
229
|
// eslint-disable-next-line
|
|
225
|
-
private async atomicApplicationUpdate<
|
|
226
|
-
func:
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
) {
|
|
230
|
+
private async atomicApplicationUpdate <F extends () => any>(
|
|
231
|
+
func: F,
|
|
232
|
+
projectId: string,
|
|
233
|
+
skipUpdateShadow?: boolean
|
|
234
|
+
): Promise<ReturnType<F>> {
|
|
230
235
|
// First try to create a backup, so that there is one available if something goes wrong in the next try:catch.
|
|
231
236
|
if (await AgentConfigFile().isAppPresent({ projectId })) {
|
|
232
237
|
try {
|
|
@@ -239,9 +244,9 @@ export class DeviceAgentCloudConnection {
|
|
|
239
244
|
}
|
|
240
245
|
|
|
241
246
|
try {
|
|
242
|
-
const out:
|
|
243
|
-
|
|
244
|
-
|
|
247
|
+
const out: ReturnType<F> = await func();
|
|
248
|
+
if (!skipUpdateShadow)
|
|
249
|
+
await this.shadowHandler.updateProjectShadow(projectId);
|
|
245
250
|
return out;
|
|
246
251
|
} catch (errorAppUpdate) {
|
|
247
252
|
logger.error(
|
|
@@ -256,9 +261,9 @@ export class DeviceAgentCloudConnection {
|
|
|
256
261
|
} catch (errorRollbackApp) {
|
|
257
262
|
// and if that fails, uninstall the app as a last resort.
|
|
258
263
|
try {
|
|
259
|
-
await
|
|
260
|
-
}
|
|
261
|
-
|
|
264
|
+
await this.atomicApplicationUninstall(projectId);
|
|
265
|
+
} catch {
|
|
266
|
+
// atomicApplicationUninstall handles failing, so there's nothing to handle here.
|
|
262
267
|
}
|
|
263
268
|
logger.error(
|
|
264
269
|
`Application update failed, rolled back to previous version: ${errorAppUpdate}`
|
|
@@ -273,15 +278,14 @@ export class DeviceAgentCloudConnection {
|
|
|
273
278
|
}
|
|
274
279
|
}
|
|
275
280
|
|
|
276
|
-
private
|
|
281
|
+
private handleProjectShadowConfigUpdate = async (
|
|
277
282
|
update: ShadowUpdate,
|
|
278
283
|
txId: string
|
|
279
284
|
): Promise<boolean> => {
|
|
280
285
|
const { projectId, appCfgUpdate, envVarUpdate } = update;
|
|
281
286
|
|
|
282
287
|
if (
|
|
283
|
-
appCfgUpdate &&
|
|
284
|
-
appCfgUpdate.updatedModels &&
|
|
288
|
+
appCfgUpdate?.updatedModels &&
|
|
285
289
|
Object.keys(appCfgUpdate.updatedModels).length
|
|
286
290
|
) {
|
|
287
291
|
// When there are model updates request signed URLs and wait to apply config changes
|
|
@@ -305,33 +309,97 @@ export class DeviceAgentCloudConnection {
|
|
|
305
309
|
);
|
|
306
310
|
this.publisher.publishToCloud(message);
|
|
307
311
|
|
|
308
|
-
this.
|
|
312
|
+
this.txnMgr.setAppCfgUpdateToTx(txId, update);
|
|
313
|
+
|
|
309
314
|
return false;
|
|
310
315
|
}
|
|
311
316
|
|
|
312
317
|
if (appCfgUpdate) {
|
|
313
|
-
await this.atomicApplicationUpdate(
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
);
|
|
318
|
+
await this.atomicApplicationUpdate(async () => {
|
|
319
|
+
await pruneModels({
|
|
320
|
+
projectId,
|
|
321
|
+
appCfg: appCfgUpdate.newAppCfg
|
|
322
|
+
});
|
|
323
|
+
await updateAppCfg({
|
|
324
|
+
projectId,
|
|
325
|
+
newAppCfg: appCfgUpdate.newAppCfg
|
|
326
|
+
});
|
|
327
|
+
}, projectId);
|
|
323
328
|
}
|
|
324
329
|
|
|
325
330
|
if (envVarUpdate) {
|
|
326
331
|
await this.atomicApplicationUpdate(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
332
|
+
async () =>
|
|
333
|
+
await this.shadowHandler.updateProjectEnvVars({
|
|
334
|
+
projectId,
|
|
335
|
+
envVars: envVarUpdate.envVars
|
|
336
|
+
}),
|
|
337
|
+
projectId,
|
|
338
|
+
true
|
|
330
339
|
);
|
|
331
340
|
}
|
|
332
341
|
return true;
|
|
333
342
|
};
|
|
334
343
|
|
|
344
|
+
private async handleProjectShadowMessage(topic: string, message: any) {
|
|
345
|
+
const shadowUpdates = await this.shadowHandler.handleProjectShadow({
|
|
346
|
+
topic,
|
|
347
|
+
payload: message,
|
|
348
|
+
clientToken: message.clientToken
|
|
349
|
+
});
|
|
350
|
+
if (shadowUpdates.length) {
|
|
351
|
+
const shadowUpdatePromises: Promise<void>[] = [];
|
|
352
|
+
for (const shadowUpdate of shadowUpdates) {
|
|
353
|
+
const projectId = shadowUpdate.projectId;
|
|
354
|
+
const txId = shadowUpdate.txId;
|
|
355
|
+
shadowUpdatePromises.push(
|
|
356
|
+
this.txnMgr
|
|
357
|
+
.runTransactionStep({
|
|
358
|
+
func: () =>
|
|
359
|
+
this.handleProjectShadowConfigUpdate(shadowUpdate, txId),
|
|
360
|
+
projectId,
|
|
361
|
+
txId,
|
|
362
|
+
start: true,
|
|
363
|
+
stepName: topic
|
|
364
|
+
})
|
|
365
|
+
.catch((e: Error) => {
|
|
366
|
+
logger.error(
|
|
367
|
+
`There was an issue updating project shadow config: ${JSON.stringify(
|
|
368
|
+
e
|
|
369
|
+
)}`
|
|
370
|
+
);
|
|
371
|
+
})
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
await Promise.all(shadowUpdatePromises);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
public async handleSecureTunnelMessage(payload: any): Promise<void> {
|
|
380
|
+
logger.info(`Received secure tunnel update: ${JSON.stringify(payload)}`);
|
|
381
|
+
const state = payload.state;
|
|
382
|
+
if (!state) {
|
|
383
|
+
logger.debug(`No state found in message: ${JSON.stringify(payload)}`);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const valid = validateSecureTunnelShadowUpdate(state);
|
|
387
|
+
if (!valid) {
|
|
388
|
+
logger.error(
|
|
389
|
+
`Error validating message: ${JSON.stringify(
|
|
390
|
+
{ payload, errors: validateSecureTunnelShadowUpdate.errors },
|
|
391
|
+
null,
|
|
392
|
+
2
|
|
393
|
+
)}`
|
|
394
|
+
);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const secureTunnelUpdate =
|
|
398
|
+
await this.secureTunnelHandler.syncShadowToDeviceState(payload);
|
|
399
|
+
await this.shadowHandler.updateSecureTunnelShadow(secureTunnelUpdate);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
335
403
|
/*=================================================================
|
|
336
404
|
Public interface
|
|
337
405
|
=================================================================*/
|
|
@@ -359,11 +427,11 @@ export class DeviceAgentCloudConnection {
|
|
|
359
427
|
|
|
360
428
|
this.subscribe(this.toDeviceTopic);
|
|
361
429
|
this.subscribe(this.secureTunnelNotifyTopic);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
this.subscribe(this.shadowHandler.shadowTopics.
|
|
366
|
-
this.subscribe(this.shadowHandler.shadowTopics.
|
|
430
|
+
for (const topic of this.shadowHandler.projectShadowTopics) {
|
|
431
|
+
this.subscribe(topic);
|
|
432
|
+
}
|
|
433
|
+
this.subscribe(this.shadowHandler.shadowTopics.secureTunnel.updateDelta);
|
|
434
|
+
this.subscribe(this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted);
|
|
367
435
|
}
|
|
368
436
|
|
|
369
437
|
public getClientId(): string {
|
|
@@ -374,10 +442,6 @@ export class DeviceAgentCloudConnection {
|
|
|
374
442
|
return this.toDeviceTopic;
|
|
375
443
|
}
|
|
376
444
|
|
|
377
|
-
public getShadowTopics(): ShadowTopics {
|
|
378
|
-
return this.shadowHandler.shadowTopics;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
445
|
public isCmdInProgress(projectId: string): boolean {
|
|
382
446
|
return this.txnMgr.isOngoingTransactionForProjectID(projectId);
|
|
383
447
|
}
|
|
@@ -412,7 +476,8 @@ export class DeviceAgentCloudConnection {
|
|
|
412
476
|
live_state_updates,
|
|
413
477
|
app_install_response,
|
|
414
478
|
models_install_response,
|
|
415
|
-
status_response
|
|
479
|
+
status_response,
|
|
480
|
+
device_action
|
|
416
481
|
} = keyMirrors.toDeviceAgentMessageType;
|
|
417
482
|
switch (message.messageType) {
|
|
418
483
|
case app_state_control: {
|
|
@@ -492,7 +557,8 @@ export class DeviceAgentCloudConnection {
|
|
|
492
557
|
);
|
|
493
558
|
}
|
|
494
559
|
await this.txnMgr.runTransactionStep({
|
|
495
|
-
func: () =>
|
|
560
|
+
func: () =>
|
|
561
|
+
this.handleModelsInstallCloudResponsePayload(payload, txId),
|
|
496
562
|
projectId,
|
|
497
563
|
txId,
|
|
498
564
|
start: false,
|
|
@@ -520,8 +586,20 @@ export class DeviceAgentCloudConnection {
|
|
|
520
586
|
}
|
|
521
587
|
break;
|
|
522
588
|
}
|
|
523
|
-
case
|
|
589
|
+
case device_action: {
|
|
524
590
|
try {
|
|
591
|
+
const statusResponsePayload: StatusResponsePayload = {
|
|
592
|
+
status: keyMirrors.statusResponse.in_progress
|
|
593
|
+
};
|
|
594
|
+
const statusResponseMessage = buildToClientStatusResponseMessage(
|
|
595
|
+
this.clientId,
|
|
596
|
+
statusResponsePayload,
|
|
597
|
+
txId
|
|
598
|
+
);
|
|
599
|
+
this.publisher.publishToClient(statusResponseMessage);
|
|
600
|
+
|
|
601
|
+
await this.handleDeviceAction(message.payload);
|
|
602
|
+
|
|
525
603
|
const successStatusResponsePayload: StatusResponsePayload = {
|
|
526
604
|
status: keyMirrors.statusResponse.success
|
|
527
605
|
};
|
|
@@ -532,8 +610,6 @@ export class DeviceAgentCloudConnection {
|
|
|
532
610
|
txId
|
|
533
611
|
);
|
|
534
612
|
this.publisher.publishToClient(successStatusResponseMessage);
|
|
535
|
-
|
|
536
|
-
await this.handleDeviceAction(message.payload);
|
|
537
613
|
} catch (e) {
|
|
538
614
|
logger.error(
|
|
539
615
|
`There was a problem performing device action '${message.payload.action}': ${e.message}`
|
|
@@ -563,57 +639,33 @@ export class DeviceAgentCloudConnection {
|
|
|
563
639
|
}
|
|
564
640
|
}
|
|
565
641
|
|
|
566
|
-
public async handleMessage(
|
|
567
|
-
topic: string,
|
|
568
|
-
message: ToDeviceAgentMessage | any
|
|
569
|
-
) {
|
|
642
|
+
public async handleMessage(topic: string, message: any) {
|
|
570
643
|
logger.debug(
|
|
571
644
|
`Received message: ${JSON.stringify({ topic, message }, null, 2)}`
|
|
572
645
|
);
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
break;
|
|
599
|
-
}
|
|
600
|
-
case this.shadowHandler.shadowTopics.projects.getRejected:
|
|
601
|
-
case this.shadowHandler.shadowTopics.projects.updateDelta:
|
|
602
|
-
case this.shadowHandler.shadowTopics.projects.updateRejected:
|
|
603
|
-
// Not handling these for now
|
|
604
|
-
break;
|
|
605
|
-
case this.toDeviceTopic:
|
|
606
|
-
await this.handleDeviceAgentMessage({
|
|
607
|
-
topic,
|
|
608
|
-
message
|
|
609
|
-
});
|
|
610
|
-
break;
|
|
611
|
-
|
|
612
|
-
case this.secureTunnelNotifyTopic:
|
|
613
|
-
await secureTunnelNotifyHandler(message);
|
|
614
|
-
break;
|
|
615
|
-
default:
|
|
616
|
-
logger.error(`Unexpected topic, ignoring! ${topic}`);
|
|
646
|
+
// ProjectShadow messages
|
|
647
|
+
if (this.shadowHandler.projectShadowTopics.includes(topic)) {
|
|
648
|
+
await this.handleProjectShadowMessage(topic, message);
|
|
649
|
+
} else if (topic === this.toDeviceTopic) {
|
|
650
|
+
await this.handleDeviceAgentMessage({
|
|
651
|
+
topic,
|
|
652
|
+
message
|
|
653
|
+
});
|
|
654
|
+
// SecureTunnelNotify messages
|
|
655
|
+
} else if (topic === this.secureTunnelNotifyTopic) {
|
|
656
|
+
await this.secureTunnelHandler.secureTunnelNotifyHandler(message);
|
|
657
|
+
// SecureTunnel messages
|
|
658
|
+
} else if (
|
|
659
|
+
topic === this.shadowHandler.shadowTopics.secureTunnel.updateDelta
|
|
660
|
+
) {
|
|
661
|
+
await this.handleSecureTunnelMessage(message);
|
|
662
|
+
} else if (
|
|
663
|
+
topic === this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted
|
|
664
|
+
) {
|
|
665
|
+
logger.info(`Received secure tunnel deleteAccepted: ${message}`);
|
|
666
|
+
await this.secureTunnelHandler.destroy();
|
|
667
|
+
} else {
|
|
668
|
+
logger.error(`Unexpected topic, ignoring! ${topic}`);
|
|
617
669
|
}
|
|
618
670
|
}
|
|
619
671
|
|
|
@@ -710,15 +762,18 @@ export class DeviceAgentCloudConnection {
|
|
|
710
762
|
}
|
|
711
763
|
|
|
712
764
|
public async stop() {
|
|
713
|
-
//
|
|
714
|
-
//
|
|
715
|
-
//
|
|
765
|
+
// This method is currently only used by the CLI, and shadow messages can be
|
|
766
|
+
// lost since we aren't waiting for responses so sleep for a short time to
|
|
767
|
+
// receive them
|
|
716
768
|
await sleep(1000);
|
|
717
769
|
this.device.end();
|
|
718
770
|
}
|
|
719
771
|
}
|
|
720
772
|
|
|
721
773
|
export async function runDeviceAgentCloudInterface() {
|
|
774
|
+
logger.info(
|
|
775
|
+
`Starting alwaysAI Device Agent v${await getDeviceAgentVersion()}`
|
|
776
|
+
);
|
|
722
777
|
if (cloudModeReady()) {
|
|
723
778
|
const deviceAgent = new DeviceAgentCloudConnection();
|
|
724
779
|
await deviceAgent.setupHandlers();
|
|
@@ -726,7 +781,6 @@ export async function runDeviceAgentCloudInterface() {
|
|
|
726
781
|
const publisher = deviceAgent.publisher;
|
|
727
782
|
const passthroughHandler = new PassthroughHandler(publisher);
|
|
728
783
|
await passthroughHandler.setup();
|
|
729
|
-
await runChannel(passthroughHandler);
|
|
730
784
|
}
|
|
731
785
|
} else if (existsSync(BOOTSTRAP_PRIVATE_KEY_FILE_PATH())) {
|
|
732
786
|
await bootstrapProvision();
|