@alwaysai/device-agent 2.0.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.
- package/lib/application-control/environment-variables.d.ts +4 -0
- package/lib/application-control/environment-variables.d.ts.map +1 -1
- package/lib/application-control/environment-variables.js +17 -13
- package/lib/application-control/environment-variables.js.map +1 -1
- package/lib/application-control/install.d.ts +4 -1
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +16 -1
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/utils.d.ts.map +1 -1
- package/lib/application-control/utils.js +13 -0
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/base-message-handler.d.ts +27 -0
- package/lib/cloud-connection/base-message-handler.d.ts.map +1 -0
- package/lib/cloud-connection/base-message-handler.js +72 -0
- package/lib/cloud-connection/base-message-handler.js.map +1 -0
- package/lib/cloud-connection/connection-manager.d.ts +21 -0
- package/lib/cloud-connection/connection-manager.d.ts.map +1 -0
- package/lib/cloud-connection/connection-manager.js +158 -0
- package/lib/cloud-connection/connection-manager.js.map +1 -0
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +7 -23
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +49 -517
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/device-agent-message-handler.d.ts +22 -0
- package/lib/cloud-connection/device-agent-message-handler.d.ts.map +1 -0
- package/lib/cloud-connection/device-agent-message-handler.js +357 -0
- package/lib/cloud-connection/device-agent-message-handler.js.map +1 -0
- package/lib/cloud-connection/live-updates-handler.d.ts +1 -0
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +13 -10
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/message-dispatcher.d.ts +10 -0
- package/lib/cloud-connection/message-dispatcher.d.ts.map +1 -0
- package/lib/cloud-connection/message-dispatcher.js +27 -0
- package/lib/cloud-connection/message-dispatcher.js.map +1 -0
- package/lib/cloud-connection/shadow-handler.d.ts +6 -0
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +74 -1
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.d.ts +8 -1
- package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
- package/lib/cloud-connection/transaction-manager.js +21 -9
- package/lib/cloud-connection/transaction-manager.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.test.js +43 -2
- package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
- package/lib/jobs/job-handler.d.ts +23 -0
- package/lib/jobs/job-handler.d.ts.map +1 -0
- package/lib/jobs/job-handler.js +131 -0
- package/lib/jobs/job-handler.js.map +1 -0
- package/lib/secure-tunneling/secure-tunnel-message-handler.d.ts +8 -0
- package/lib/secure-tunneling/secure-tunnel-message-handler.d.ts.map +1 -0
- package/lib/secure-tunneling/secure-tunnel-message-handler.js +42 -0
- package/lib/secure-tunneling/secure-tunnel-message-handler.js.map +1 -0
- package/lib/subcommands/app/version.d.ts +2 -0
- package/lib/subcommands/app/version.d.ts.map +1 -1
- package/lib/subcommands/app/version.js +14 -2
- package/lib/subcommands/app/version.js.map +1 -1
- package/package.json +2 -2
- package/src/application-control/environment-variables.ts +31 -21
- package/src/application-control/install.ts +24 -3
- package/src/application-control/utils.ts +13 -0
- package/src/cloud-connection/base-message-handler.ts +118 -0
- package/src/cloud-connection/connection-manager.ts +187 -0
- package/src/cloud-connection/device-agent-cloud-connection.ts +109 -816
- package/src/cloud-connection/device-agent-message-handler.ts +642 -0
- package/src/cloud-connection/live-updates-handler.ts +26 -18
- package/src/cloud-connection/message-dispatcher.ts +33 -0
- package/src/cloud-connection/shadow-handler.ts +125 -1
- package/src/cloud-connection/transaction-manager.test.ts +65 -3
- package/src/cloud-connection/transaction-manager.ts +41 -27
- package/src/jobs/job-handler.ts +146 -0
- package/src/secure-tunneling/secure-tunnel-message-handler.ts +56 -0
- package/src/subcommands/app/version.ts +20 -2
|
@@ -1,878 +1,171 @@
|
|
|
1
|
-
// eslint-disable-next-line
|
|
2
|
-
const awsIot = require('aws-iot-device-sdk');
|
|
3
1
|
import {
|
|
4
|
-
AppInstallResponsePayload,
|
|
5
|
-
AppStateControlPayload,
|
|
6
|
-
AppVersionControlInstallPayload,
|
|
7
|
-
AppVersionControlUninstallPayload,
|
|
8
|
-
DeviceActionPayload,
|
|
9
|
-
ModelsInstallResponsePayload,
|
|
10
|
-
SignedUrlsRequestPayload,
|
|
11
|
-
ToCloudMessage,
|
|
12
|
-
ToDeviceAgentMessage,
|
|
13
|
-
buildAppLogsMessage,
|
|
14
|
-
buildAppStateMessage,
|
|
15
|
-
buildDeviceStatsMessage,
|
|
16
|
-
buildSignedUrlsRequestMessage,
|
|
17
2
|
buildToClientStatusResponseMessage,
|
|
18
3
|
getToDeviceTopic,
|
|
19
|
-
|
|
20
|
-
keyMirrors,
|
|
21
|
-
validateSecureTunnelShadowUpdate,
|
|
22
|
-
validateToDeviceAgentMessage
|
|
4
|
+
keyMirrors
|
|
23
5
|
} from '@alwaysai/device-agent-schemas';
|
|
24
|
-
import {
|
|
25
|
-
DEVICE_CERTIFICATE_FILE_PATH,
|
|
26
|
-
DEVICE_PRIVATE_KEY_FILE_PATH
|
|
27
|
-
} from 'alwaysai/lib/infrastructure';
|
|
28
|
-
import { stringifyError } from 'alwaysai/lib/util';
|
|
29
|
-
import { exec } from 'child_process';
|
|
30
6
|
import { existsSync } from 'fs';
|
|
31
|
-
import { promisify } from 'util';
|
|
32
|
-
import {
|
|
33
|
-
getAppLogs,
|
|
34
|
-
installApp,
|
|
35
|
-
restartApp,
|
|
36
|
-
startApp,
|
|
37
|
-
stopApp,
|
|
38
|
-
uninstallApp,
|
|
39
|
-
updateAppCfg,
|
|
40
|
-
updateModelsWithPresignedUrls
|
|
41
|
-
} from '../application-control';
|
|
42
|
-
import { createAppBackup, rollbackApp } from '../application-control/backup';
|
|
43
|
-
import { pruneModels } from '../application-control/models';
|
|
44
|
-
import { reboot } from '../device-control/device-control';
|
|
45
7
|
import { ALWAYSAI_ANALYTICS_PASSTHROUGH } from '../environment';
|
|
46
|
-
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
47
8
|
import { getBootstrapPrivateKeyFilePath } from '../infrastructure/device-certificate';
|
|
48
9
|
import { migrateFromLegacyCertsAndTokens } from '../infrastructure/legacy-migration/legacy-migration';
|
|
49
10
|
import { requiredConfigFilesPresentAndValid } from '../infrastructure/required-config-checks';
|
|
50
11
|
import { getIoTCoreEndpointUrl } from '../infrastructure/urls';
|
|
51
12
|
import { SecureTunnelHandlerSingleton } from '../secure-tunneling/secure-tunneling';
|
|
52
|
-
import
|
|
13
|
+
import { SecureTunnelMessageHandler } from '../secure-tunneling/secure-tunnel-message-handler';
|
|
53
14
|
import { getDeviceAgentVersion } from '../util/check-for-updates';
|
|
54
|
-
import { AWS_ROOT_CERTIFICATE_FILE_PATH } from '../util/directories';
|
|
55
15
|
import { getDeviceUuid } from '../util/get-device-id';
|
|
56
16
|
import { logger } from '../util/logger';
|
|
57
17
|
import sleep from '../util/sleep';
|
|
58
18
|
import { bootstrapProvision } from './bootstrap-provision';
|
|
59
19
|
import { LiveUpdatesHandler } from './live-updates-handler';
|
|
60
|
-
import { getAppStatePayload, getDeviceStatsPayload } from './messages';
|
|
61
20
|
import { PassthroughHandler } from './passthrough-handler';
|
|
62
21
|
import { Publisher } from './publisher';
|
|
63
|
-
import { ShadowHandler,
|
|
22
|
+
import { ShadowHandler, ProjectShadowMessageHandler } from './shadow-handler';
|
|
23
|
+
import { JobHandler } from '../jobs/job-handler';
|
|
64
24
|
import { TransactionManager } from './transaction-manager';
|
|
65
|
-
|
|
66
|
-
|
|
25
|
+
import { ConnectionManager } from './connection-manager';
|
|
26
|
+
import { DeviceAgentMessageHandler } from './device-agent-message-handler';
|
|
27
|
+
import { HandlerContext } from './base-message-handler';
|
|
67
28
|
|
|
68
29
|
export class DeviceAgentCloudConnection {
|
|
30
|
+
private connectionManager: ConnectionManager;
|
|
31
|
+
private transactionManager: TransactionManager;
|
|
32
|
+
private liveUpdatesHandler: LiveUpdatesHandler;
|
|
69
33
|
public shadowHandler: ShadowHandler;
|
|
70
34
|
public publisher: Publisher;
|
|
71
|
-
private liveUpdatesHandler: LiveUpdatesHandler;
|
|
72
|
-
private txnMgr: TransactionManager;
|
|
73
|
-
private device = awsIot.device;
|
|
74
35
|
|
|
75
|
-
private clientId = getDeviceUuid();
|
|
76
|
-
private host = getIoTCoreEndpointUrl();
|
|
77
|
-
private port = 8883;
|
|
36
|
+
private readonly clientId = getDeviceUuid();
|
|
37
|
+
private readonly host = getIoTCoreEndpointUrl();
|
|
38
|
+
private readonly port = 8883;
|
|
78
39
|
private readonly toDeviceTopic = getToDeviceTopic(this.clientId);
|
|
79
|
-
private readonly secureTunnelNotifyTopic = `$aws/things/${this.clientId}/tunnels/notify`;
|
|
80
40
|
private readonly secureTunnelHandler =
|
|
81
41
|
SecureTunnelHandlerSingleton.getInstance();
|
|
82
42
|
|
|
83
43
|
constructor() {
|
|
84
|
-
this.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
44
|
+
this.liveUpdatesHandler = new LiveUpdatesHandler();
|
|
45
|
+
|
|
46
|
+
// Initialize & setup the connection
|
|
47
|
+
this.connectionManager = new ConnectionManager(
|
|
48
|
+
this.clientId,
|
|
49
|
+
this.host,
|
|
50
|
+
this.port
|
|
51
|
+
);
|
|
52
|
+
this.connectionManager.setupConnection();
|
|
93
53
|
|
|
94
|
-
|
|
54
|
+
// Initialize the Publisher, ShadowHandler & Transaction Manager
|
|
55
|
+
this.publisher = new Publisher(
|
|
56
|
+
this.connectionManager.getIoTDevice(),
|
|
57
|
+
this.clientId
|
|
58
|
+
);
|
|
95
59
|
this.shadowHandler = new ShadowHandler(this.clientId, this.publisher);
|
|
96
|
-
|
|
97
|
-
this.
|
|
60
|
+
|
|
61
|
+
this.transactionManager = new TransactionManager(
|
|
98
62
|
this.publisher,
|
|
99
63
|
this.liveUpdatesHandler
|
|
100
64
|
);
|
|
101
65
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
this.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/*=================================================================
|
|
114
|
-
Public interface
|
|
115
|
-
=================================================================*/
|
|
116
|
-
|
|
117
|
-
public getClientId(): string {
|
|
118
|
-
return this.clientId;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
public isCmdInProgress(projectId: string): boolean {
|
|
122
|
-
return this.txnMgr.isOngoingTransactionForProjectID(projectId);
|
|
123
|
-
}
|
|
66
|
+
// Construct a HandlerContext used by all the message handlers
|
|
67
|
+
const handlerContext: HandlerContext = {
|
|
68
|
+
clientId: this.clientId,
|
|
69
|
+
txnMgr: this.transactionManager,
|
|
70
|
+
publisher: this.publisher,
|
|
71
|
+
shadowHandler: this.shadowHandler,
|
|
72
|
+
liveUpdatesHandler: this.liveUpdatesHandler,
|
|
73
|
+
secureTunnelHandler: this.secureTunnelHandler
|
|
74
|
+
};
|
|
124
75
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
76
|
+
// Instantiate & register message handlers for Project Shadow topics
|
|
77
|
+
const projectShadowMessageHandler = new ProjectShadowMessageHandler(
|
|
78
|
+
handlerContext
|
|
128
79
|
);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
await this.handleProjectShadowMessage(topic, message);
|
|
132
|
-
} else if (topic === this.toDeviceTopic) {
|
|
133
|
-
await this.handleDeviceAgentMessage({
|
|
80
|
+
this.shadowHandler.projectShadowTopics.forEach((topic) => {
|
|
81
|
+
this.connectionManager.registerHandler(
|
|
134
82
|
topic,
|
|
135
|
-
|
|
136
|
-
});
|
|
137
|
-
// SecureTunnelNotify messages
|
|
138
|
-
} else if (topic === this.secureTunnelNotifyTopic) {
|
|
139
|
-
await this.secureTunnelHandler.secureTunnelNotifyHandler(message);
|
|
140
|
-
// SecureTunnel messages
|
|
141
|
-
} else if (
|
|
142
|
-
topic === this.shadowHandler.shadowTopics.secureTunnel.updateDelta
|
|
143
|
-
) {
|
|
144
|
-
await this.handleSecureTunnelMessage(message);
|
|
145
|
-
} else if (
|
|
146
|
-
topic === this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted
|
|
147
|
-
) {
|
|
148
|
-
logger.info(`Received secure tunnel deleteAccepted: ${message}`);
|
|
149
|
-
await this.secureTunnelHandler.destroy();
|
|
150
|
-
} else {
|
|
151
|
-
logger.error(`Unexpected topic, ignoring! ${topic}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
public async stop() {
|
|
156
|
-
// This method is currently only used by the CLI, and shadow messages can be
|
|
157
|
-
// lost since we aren't waiting for responses so sleep for a short time to
|
|
158
|
-
// receive them
|
|
159
|
-
await sleep(1000);
|
|
160
|
-
this.device.end();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/*=================================================================
|
|
164
|
-
Private interface
|
|
165
|
-
=================================================================*/
|
|
166
|
-
|
|
167
|
-
private setupHandlers() {
|
|
168
|
-
this.device.on('connect', (connack: any) => {
|
|
169
|
-
logger.info('Device Agent has connected to the cloud');
|
|
170
|
-
// FIXME: EI-709 Skip this request for now to prevent kicking off another
|
|
171
|
-
// shadow update process if IoT Core disconnect occurs during app config update
|
|
172
|
-
//this.shadowHandler.getShadowUpdates();
|
|
173
|
-
void this.shadowHandler.updateSystemInfoShadow();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
this.device.on('disconnect', () => {
|
|
177
|
-
logger.warn('Device Agent has been disconnected from the cloud');
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
this.device.on('reconnect', () => {
|
|
181
|
-
logger.info(
|
|
182
|
-
`Device Agent attempting to re-connect ${new Date().toLocaleString()}`
|
|
83
|
+
projectShadowMessageHandler
|
|
183
84
|
);
|
|
184
85
|
});
|
|
185
86
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
this.device.on('offline', () => {
|
|
200
|
-
logger.warn(`Device Agent is offline ${new Date().toLocaleString()}`);
|
|
201
|
-
void this.logConnectionInfo();
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
private async logConnectionInfo() {
|
|
206
|
-
try {
|
|
207
|
-
/**
|
|
208
|
-
* We're using the 'netcat' or 'nc' command to test the connection to the IoT Core endpoint.
|
|
209
|
-
* This command doesn't always exit (see below), so
|
|
210
|
-
* we use timeout to break out of the prompt
|
|
211
|
-
* and catch the resulting error/parse the resulting stderr
|
|
212
|
-
*
|
|
213
|
-
* Sample command for current host and port:
|
|
214
|
-
* nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 8883
|
|
215
|
-
*
|
|
216
|
-
* Sample output when port is not blocked and host is reachable:
|
|
217
|
-
* $ nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 443
|
|
218
|
-
* Connection to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 443 port [tcp/https] succeeded!
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* Sample output when port is blocked (will repeatedly try until ctrl-C out):
|
|
222
|
-
* $ nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 8883
|
|
223
|
-
* nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
|
|
224
|
-
* nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
|
|
225
|
-
* nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
|
|
226
|
-
* ^C
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
* Sample command/output when the port isn't enable on that host:
|
|
230
|
-
* $ nc -zv -w 1 localhost 8883
|
|
231
|
-
* nc: connect to localhost port 8883 (tcp) failed: Connection refused
|
|
232
|
-
*/
|
|
233
|
-
await exec_promise(`nc -zv -w 1 ${this.host} ${this.port}`, {
|
|
234
|
-
timeout: 2000
|
|
235
|
-
});
|
|
236
|
-
} catch (err) {
|
|
237
|
-
const output = JSON.stringify(err['stderr']);
|
|
238
|
-
if (output.indexOf('not known') !== -1) {
|
|
239
|
-
logger.warn(
|
|
240
|
-
'Iot Core endpoint appears to be unreachable, internet connection may be unstable or the host may be down.'
|
|
241
|
-
);
|
|
242
|
-
} else if (output.indexOf('timed out') !== -1) {
|
|
243
|
-
logger.warn(
|
|
244
|
-
`Internet connection appears fine, however the endpoint was not reachable on the current connection port: ${this.port}\nPlease check if a firewall is in place.`
|
|
245
|
-
);
|
|
246
|
-
} else if (output.indexOf('refused') !== -1) {
|
|
247
|
-
logger.warn(
|
|
248
|
-
`The connection was refused, likely ${this.host} is not running a service on ${this.port}.`
|
|
249
|
-
);
|
|
250
|
-
} else {
|
|
251
|
-
logger.warn(
|
|
252
|
-
`Output from checking connection to ${this.host} on ${this.port}: ${output}`
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
private async handleDeviceAgentMessage({
|
|
259
|
-
topic,
|
|
260
|
-
message
|
|
261
|
-
}: {
|
|
262
|
-
topic: string;
|
|
263
|
-
message: ToDeviceAgentMessage;
|
|
264
|
-
}) {
|
|
265
|
-
const valid = validateToDeviceAgentMessage(message);
|
|
266
|
-
if (!valid) {
|
|
267
|
-
logger.error(
|
|
268
|
-
`Error validating message: ${JSON.stringify(
|
|
269
|
-
{ topic, message, errors: validateToDeviceAgentMessage.errors },
|
|
270
|
-
null,
|
|
271
|
-
2
|
|
272
|
-
)}`
|
|
273
|
-
);
|
|
274
|
-
// TODO: Send generic error response
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
const txId = message.txId;
|
|
278
|
-
const {
|
|
279
|
-
app_state_control,
|
|
280
|
-
app_version_control,
|
|
281
|
-
live_state_updates,
|
|
282
|
-
app_install_response,
|
|
283
|
-
models_install_response,
|
|
284
|
-
status_response,
|
|
285
|
-
device_action
|
|
286
|
-
} = keyMirrors.toDeviceAgentMessageType;
|
|
287
|
-
switch (message.messageType) {
|
|
288
|
-
case app_state_control: {
|
|
289
|
-
// txId sent from cloud, just need to continue it
|
|
290
|
-
const payload = message.payload;
|
|
291
|
-
const projectId = payload.projectId;
|
|
292
|
-
|
|
293
|
-
try {
|
|
294
|
-
await this.txnMgr.runTransactionStep({
|
|
295
|
-
func: () => this.handleAppStateControl(message.payload),
|
|
296
|
-
projectId,
|
|
297
|
-
txId,
|
|
298
|
-
start: true,
|
|
299
|
-
liveUpdatesPublishFn: async () =>
|
|
300
|
-
this.publisher.publishToClient(
|
|
301
|
-
buildToClientStatusResponseMessage(
|
|
302
|
-
this.clientId,
|
|
303
|
-
{ status: keyMirrors.statusResponse.in_progress },
|
|
304
|
-
txId
|
|
305
|
-
),
|
|
306
|
-
logger.silly
|
|
307
|
-
),
|
|
308
|
-
stepName: payload.baseCommand
|
|
309
|
-
});
|
|
310
|
-
} catch (e) {
|
|
311
|
-
logger.error(
|
|
312
|
-
`Error processing application state control request for ${projectId}!\n${stringifyError(
|
|
313
|
-
e
|
|
314
|
-
)}`
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
break;
|
|
319
|
-
}
|
|
320
|
-
case app_version_control: {
|
|
321
|
-
// txId sent from cloud, just need to continue it
|
|
322
|
-
const payload = message.payload;
|
|
323
|
-
const projectId = payload.projectId;
|
|
324
|
-
try {
|
|
325
|
-
await this.txnMgr.runTransactionStep({
|
|
326
|
-
func: () => this.handleAppVersionControl(payload, txId),
|
|
327
|
-
projectId,
|
|
328
|
-
txId,
|
|
329
|
-
start: true,
|
|
330
|
-
liveUpdatesPublishFn: async () =>
|
|
331
|
-
this.publisher.publishToClient(
|
|
332
|
-
buildToClientStatusResponseMessage(
|
|
333
|
-
this.clientId,
|
|
334
|
-
{ status: keyMirrors.statusResponse.in_progress },
|
|
335
|
-
txId
|
|
336
|
-
),
|
|
337
|
-
logger.silly
|
|
338
|
-
),
|
|
339
|
-
stepName: payload.baseCommand
|
|
340
|
-
});
|
|
341
|
-
} catch (e) {
|
|
342
|
-
logger.error(
|
|
343
|
-
`Error processing application install request for ${projectId}!\n${stringifyError(
|
|
344
|
-
e
|
|
345
|
-
)}`
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
break;
|
|
350
|
-
}
|
|
351
|
-
case live_state_updates: {
|
|
352
|
-
const { deviceStats, appState, appLogs } = message.payload;
|
|
353
|
-
|
|
354
|
-
if (deviceStats !== undefined) {
|
|
355
|
-
if (deviceStats) {
|
|
356
|
-
await this.liveUpdatesHandler.enable(
|
|
357
|
-
keyMirrors.toClientMessageType.device_stats,
|
|
358
|
-
async () =>
|
|
359
|
-
this.publisher.publishToClient(
|
|
360
|
-
buildDeviceStatsMessage(
|
|
361
|
-
this.clientId,
|
|
362
|
-
await getDeviceStatsPayload(),
|
|
363
|
-
txId
|
|
364
|
-
),
|
|
365
|
-
logger.silly
|
|
366
|
-
)
|
|
367
|
-
);
|
|
368
|
-
} else {
|
|
369
|
-
this.liveUpdatesHandler.disable(
|
|
370
|
-
keyMirrors.toClientMessageType.device_stats
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (appState !== undefined) {
|
|
376
|
-
if (appState) {
|
|
377
|
-
await this.liveUpdatesHandler.enable(
|
|
378
|
-
keyMirrors.toClientMessageType.app_state,
|
|
379
|
-
async () =>
|
|
380
|
-
this.publisher.publishToClient(
|
|
381
|
-
buildAppStateMessage(
|
|
382
|
-
this.clientId,
|
|
383
|
-
await getAppStatePayload(),
|
|
384
|
-
txId
|
|
385
|
-
),
|
|
386
|
-
logger.silly
|
|
387
|
-
)
|
|
388
|
-
);
|
|
389
|
-
} else {
|
|
390
|
-
this.liveUpdatesHandler.disable(
|
|
391
|
-
keyMirrors.toClientMessageType.app_state
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (appLogs !== undefined) {
|
|
397
|
-
if (appLogs.toggle) {
|
|
398
|
-
await this.liveUpdatesHandler.startStream(
|
|
399
|
-
appLogs.projectId,
|
|
400
|
-
async () =>
|
|
401
|
-
await getAppLogs({
|
|
402
|
-
projectId: appLogs.projectId,
|
|
403
|
-
args: ['--tail', '100', '--no-log-prefix']
|
|
404
|
-
}),
|
|
405
|
-
async (logChunk: string) =>
|
|
406
|
-
this.publisher.publishToClient(
|
|
407
|
-
buildAppLogsMessage(
|
|
408
|
-
this.clientId,
|
|
409
|
-
{
|
|
410
|
-
projectId: appLogs.projectId,
|
|
411
|
-
logChunk
|
|
412
|
-
},
|
|
413
|
-
txId
|
|
414
|
-
),
|
|
415
|
-
logger.silly
|
|
416
|
-
)
|
|
417
|
-
);
|
|
418
|
-
} else {
|
|
419
|
-
this.liveUpdatesHandler.stopStream(appLogs.projectId);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
|
-
case app_install_response: {
|
|
425
|
-
const payload = message.payload;
|
|
426
|
-
const { projectId } = payload.appInstallResponse;
|
|
427
|
-
if (txId !== this.txnMgr.getTransactionFromProject(projectId)) {
|
|
428
|
-
throw new Error(
|
|
429
|
-
`App install response received a message for a transaction ID ${txId} that is not currently underway (${this.txnMgr.getTransactionFromProject(
|
|
430
|
-
projectId
|
|
431
|
-
)})!`
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
await this.txnMgr.runTransactionStep({
|
|
435
|
-
func: () => this.handleAppInstallCloudResponsePayload(payload),
|
|
436
|
-
projectId,
|
|
437
|
-
txId,
|
|
438
|
-
start: false,
|
|
439
|
-
stepName: message.messageType
|
|
440
|
-
});
|
|
441
|
-
break;
|
|
442
|
-
}
|
|
443
|
-
case models_install_response: {
|
|
444
|
-
// This message doesn't have appReleaseHash in it's payload, but
|
|
445
|
-
// atomicCmd should be able to read it from the installed app
|
|
446
|
-
const payload = message.payload;
|
|
447
|
-
const { projectId } = payload.modelsInstallResponse;
|
|
448
|
-
if (txId !== this.txnMgr.getTransactionFromProject(projectId)) {
|
|
449
|
-
throw new Error(
|
|
450
|
-
`Model install response received a message for a transaction ID ${txId} that is not currently underway (${this.txnMgr.getTransactionFromProject(
|
|
451
|
-
projectId
|
|
452
|
-
)})!`
|
|
453
|
-
);
|
|
454
|
-
}
|
|
455
|
-
await this.txnMgr.runTransactionStep({
|
|
456
|
-
func: () =>
|
|
457
|
-
this.handleModelsInstallCloudResponsePayload(payload, txId),
|
|
458
|
-
projectId,
|
|
459
|
-
txId,
|
|
460
|
-
start: false,
|
|
461
|
-
stepName: message.messageType
|
|
462
|
-
});
|
|
463
|
-
break;
|
|
464
|
-
}
|
|
465
|
-
case status_response: {
|
|
466
|
-
const { failure } = keyMirrors.statusResponse;
|
|
467
|
-
if (message.payload.status === failure) {
|
|
468
|
-
this.txnMgr.completeTransaction(
|
|
469
|
-
txId,
|
|
470
|
-
buildToClientStatusResponseMessage(
|
|
471
|
-
this.clientId,
|
|
472
|
-
{
|
|
473
|
-
status: keyMirrors.statusResponse.failure,
|
|
474
|
-
message: message.payload.message
|
|
475
|
-
},
|
|
476
|
-
txId
|
|
477
|
-
)
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
break;
|
|
481
|
-
}
|
|
482
|
-
case device_action: {
|
|
483
|
-
try {
|
|
484
|
-
this.publisher.publishToClient(
|
|
485
|
-
buildToClientStatusResponseMessage(
|
|
486
|
-
this.clientId,
|
|
487
|
-
{
|
|
488
|
-
status: keyMirrors.statusResponse.in_progress
|
|
489
|
-
},
|
|
490
|
-
txId
|
|
491
|
-
)
|
|
492
|
-
);
|
|
493
|
-
|
|
494
|
-
await this.handleDeviceAction(message.payload);
|
|
495
|
-
|
|
496
|
-
this.publisher.publishToClient(
|
|
497
|
-
buildToClientStatusResponseMessage(
|
|
498
|
-
this.clientId,
|
|
499
|
-
{
|
|
500
|
-
status: keyMirrors.statusResponse.success
|
|
501
|
-
},
|
|
502
|
-
txId
|
|
503
|
-
)
|
|
504
|
-
);
|
|
505
|
-
} catch (e) {
|
|
506
|
-
logger.error(
|
|
507
|
-
`There was a problem performing device action '${
|
|
508
|
-
message.payload.action
|
|
509
|
-
}'!\n${stringifyError(e)}`
|
|
87
|
+
// Instantiate & register message handlers for to-device and secureTunnel topics
|
|
88
|
+
this.connectionManager.registerHandler(
|
|
89
|
+
this.toDeviceTopic,
|
|
90
|
+
new DeviceAgentMessageHandler(
|
|
91
|
+
handlerContext,
|
|
92
|
+
(txId: string, errorMsg: string) => {
|
|
93
|
+
const msg = buildToClientStatusResponseMessage(
|
|
94
|
+
this.publisher.getClientId(),
|
|
95
|
+
{
|
|
96
|
+
status: keyMirrors.statusResponse.failure,
|
|
97
|
+
message: errorMsg
|
|
98
|
+
},
|
|
99
|
+
txId
|
|
510
100
|
);
|
|
511
|
-
this.publisher.publishToClient(
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
txId
|
|
519
|
-
)
|
|
101
|
+
this.publisher.publishToClient(msg);
|
|
102
|
+
},
|
|
103
|
+
(txId: string) => {
|
|
104
|
+
const msg = buildToClientStatusResponseMessage(
|
|
105
|
+
this.publisher.getClientId(),
|
|
106
|
+
{ status: keyMirrors.statusResponse.success },
|
|
107
|
+
txId
|
|
520
108
|
);
|
|
109
|
+
this.publisher.publishToClient(msg);
|
|
521
110
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
default:
|
|
525
|
-
logger.error(
|
|
526
|
-
`Invalid client message: '${JSON.stringify(
|
|
527
|
-
{ topic, message, txId },
|
|
528
|
-
null,
|
|
529
|
-
2
|
|
530
|
-
)}'`
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
private handleAppStateControl = async (
|
|
536
|
-
payload: AppStateControlPayload
|
|
537
|
-
): Promise<boolean> => {
|
|
538
|
-
const { baseCommand, projectId } = payload;
|
|
539
|
-
switch (baseCommand) {
|
|
540
|
-
case keyMirrors.appStateControl.start:
|
|
541
|
-
await startApp({ projectId });
|
|
542
|
-
break;
|
|
543
|
-
case keyMirrors.appStateControl.stop:
|
|
544
|
-
await stopApp({ projectId });
|
|
545
|
-
break;
|
|
546
|
-
case keyMirrors.appStateControl.restart:
|
|
547
|
-
await restartApp({ projectId });
|
|
548
|
-
break;
|
|
549
|
-
}
|
|
550
|
-
return true;
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
private handleAppVersionControl = async (
|
|
554
|
-
payload:
|
|
555
|
-
| AppVersionControlInstallPayload
|
|
556
|
-
| AppVersionControlUninstallPayload,
|
|
557
|
-
txId: string
|
|
558
|
-
): Promise<boolean> => {
|
|
559
|
-
switch (payload.baseCommand) {
|
|
560
|
-
case keyMirrors.appVersionControl.install: {
|
|
561
|
-
const { projectId, appReleaseHash } = payload;
|
|
562
|
-
|
|
563
|
-
const signedUrlsRequestPayload: SignedUrlsRequestPayload = {
|
|
564
|
-
signedUrlsRequest: {
|
|
565
|
-
projectId,
|
|
566
|
-
appReleaseHash
|
|
567
|
-
}
|
|
568
|
-
};
|
|
569
|
-
const message = buildSignedUrlsRequestMessage(
|
|
570
|
-
this.clientId,
|
|
571
|
-
signedUrlsRequestPayload,
|
|
572
|
-
txId
|
|
573
|
-
);
|
|
574
|
-
await this.publishCloudRequest(message);
|
|
575
|
-
return false;
|
|
576
|
-
}
|
|
577
|
-
case keyMirrors.appVersionControl.uninstall: {
|
|
578
|
-
const { projectId } = payload;
|
|
579
|
-
await this.atomicApplicationUninstall(projectId);
|
|
580
|
-
return true;
|
|
581
|
-
}
|
|
582
|
-
default:
|
|
583
|
-
logger.warn(
|
|
584
|
-
`Ignore App Version Control packet: ${JSON.stringify(
|
|
585
|
-
payload,
|
|
586
|
-
null,
|
|
587
|
-
2
|
|
588
|
-
)}`
|
|
589
|
-
);
|
|
590
|
-
return true;
|
|
591
|
-
}
|
|
592
|
-
};
|
|
593
|
-
|
|
594
|
-
private handleAppInstallCloudResponsePayload = async (
|
|
595
|
-
payload: AppInstallResponsePayload
|
|
596
|
-
): Promise<boolean> => {
|
|
597
|
-
const {
|
|
598
|
-
projectId,
|
|
599
|
-
appReleaseHash,
|
|
600
|
-
appInstallPayload,
|
|
601
|
-
modelsInstallPayload
|
|
602
|
-
} = payload.appInstallResponse;
|
|
603
|
-
const signedUrlsPayload = {
|
|
604
|
-
appInstallPayload,
|
|
605
|
-
modelsInstallPayload
|
|
606
|
-
};
|
|
607
|
-
await this.atomicApplicationUpdate(async () => {
|
|
608
|
-
this.shadowHandler.clearProjectShadow(projectId);
|
|
609
|
-
await installApp({ projectId, appReleaseHash, signedUrlsPayload });
|
|
610
|
-
}, projectId);
|
|
611
|
-
return true;
|
|
612
|
-
};
|
|
111
|
+
)
|
|
112
|
+
);
|
|
613
113
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
114
|
+
const secureTunnelMessageHandler = new SecureTunnelMessageHandler(
|
|
115
|
+
handlerContext
|
|
116
|
+
);
|
|
117
|
+
this.connectionManager.registerHandler(
|
|
118
|
+
secureTunnelMessageHandler.getNotifyTopic(),
|
|
119
|
+
secureTunnelMessageHandler
|
|
120
|
+
);
|
|
619
121
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
await this.atomicApplicationUpdate(
|
|
629
|
-
async () =>
|
|
630
|
-
await updateModelsWithPresignedUrls({
|
|
631
|
-
projectId,
|
|
632
|
-
modelInstallPayloads: payload.modelsInstallResponse.newModels,
|
|
633
|
-
newAppCfg: appCfgUpdate.newAppCfg
|
|
634
|
-
}),
|
|
635
|
-
projectId
|
|
636
|
-
);
|
|
637
|
-
}
|
|
122
|
+
this.connectionManager.registerHandler(
|
|
123
|
+
this.shadowHandler.shadowTopics.secureTunnel.updateDelta,
|
|
124
|
+
secureTunnelMessageHandler
|
|
125
|
+
);
|
|
126
|
+
this.connectionManager.registerHandler(
|
|
127
|
+
this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted,
|
|
128
|
+
secureTunnelMessageHandler
|
|
129
|
+
);
|
|
638
130
|
|
|
639
|
-
|
|
640
|
-
await this.atomicApplicationUpdate(
|
|
641
|
-
async () =>
|
|
642
|
-
await this.shadowHandler.updateProjectEnvVars({
|
|
643
|
-
projectId,
|
|
644
|
-
envVars: envVarUpdate.envVars
|
|
645
|
-
}),
|
|
646
|
-
projectId,
|
|
647
|
-
true
|
|
648
|
-
);
|
|
649
|
-
}
|
|
131
|
+
const jobHandler = new JobHandler(handlerContext);
|
|
650
132
|
|
|
651
|
-
|
|
652
|
-
};
|
|
133
|
+
const JOB_HANDLER_TOPICS = jobHandler.getJobTopic();
|
|
653
134
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
await reboot();
|
|
659
|
-
break;
|
|
660
|
-
}
|
|
661
|
-
default: {
|
|
662
|
-
logger.info(
|
|
663
|
-
`Unrecognized device action requested: '${payload.action}'.`
|
|
664
|
-
);
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
135
|
+
this.connectionManager.registerHandler(
|
|
136
|
+
JOB_HANDLER_TOPICS.NOTIFY_NEXT,
|
|
137
|
+
jobHandler
|
|
138
|
+
);
|
|
668
139
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
140
|
+
this.connectionManager.registerHandler(
|
|
141
|
+
JOB_HANDLER_TOPICS.START_NEXT_ACCEPTED,
|
|
142
|
+
jobHandler
|
|
143
|
+
);
|
|
672
144
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
this.device.subscribe(topic);
|
|
145
|
+
// Initialize job
|
|
146
|
+
this.publisher.publish(JOB_HANDLER_TOPICS.START_NEXT, JSON.stringify({}));
|
|
676
147
|
}
|
|
677
148
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
logger.error(`Failed to uninstall ${projectId}!\n${stringifyError(e)}`);
|
|
684
|
-
throw e;
|
|
685
|
-
}
|
|
149
|
+
/*=================================================================
|
|
150
|
+
Public interface
|
|
151
|
+
=================================================================*/
|
|
152
|
+
public getClientId(): string {
|
|
153
|
+
return this.clientId;
|
|
686
154
|
}
|
|
687
155
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
func: F,
|
|
691
|
-
projectId: string,
|
|
692
|
-
skipUpdateShadow?: boolean
|
|
693
|
-
): Promise<ReturnType<F>> {
|
|
694
|
-
if (await AgentConfigFile().isAppPresent({ projectId })) {
|
|
695
|
-
// Reject the application update if app is present but not ready
|
|
696
|
-
if (!(await AgentConfigFile().isAppReady({ projectId }))) {
|
|
697
|
-
throw new Error('Application already has installation in progress!');
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// Try to create a backup, so that there is one available if something goes wrong in the next try:catch.
|
|
701
|
-
try {
|
|
702
|
-
await createAppBackup({ projectId });
|
|
703
|
-
} catch (e) {
|
|
704
|
-
logger.error(
|
|
705
|
-
`Could not create a backup for the project: ${projectId}!\n${stringifyError(
|
|
706
|
-
e
|
|
707
|
-
)}`
|
|
708
|
-
);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
try {
|
|
713
|
-
const out: ReturnType<F> = await func();
|
|
714
|
-
if (!skipUpdateShadow)
|
|
715
|
-
await this.shadowHandler.updateProjectShadow(projectId);
|
|
716
|
-
return out;
|
|
717
|
-
} catch (errorAppUpdate) {
|
|
718
|
-
logger.error(
|
|
719
|
-
`Failed to update ${projectId}!\n${stringifyError(errorAppUpdate)}`
|
|
720
|
-
);
|
|
721
|
-
// If something goes wrong, first try to rollback
|
|
722
|
-
try {
|
|
723
|
-
await rollbackApp({ projectId });
|
|
724
|
-
} catch (errorRollbackApp) {
|
|
725
|
-
logger.error(
|
|
726
|
-
`Application rollback failed for ${projectId}!\n${stringifyError(
|
|
727
|
-
errorRollbackApp
|
|
728
|
-
)}`
|
|
729
|
-
);
|
|
730
|
-
// and if that fails, uninstall the app as a last resort.
|
|
731
|
-
try {
|
|
732
|
-
await this.atomicApplicationUninstall(projectId);
|
|
733
|
-
} catch {
|
|
734
|
-
// atomicApplicationUninstall logs failure, so there's nothing to do here.
|
|
735
|
-
}
|
|
736
|
-
throw new AaiError(
|
|
737
|
-
'Application update and rollback failed, uninstalled the application!',
|
|
738
|
-
{ cause: errorAppUpdate }
|
|
739
|
-
);
|
|
740
|
-
}
|
|
741
|
-
throw new Error(
|
|
742
|
-
'Application update failed, rolled the application back!',
|
|
743
|
-
{ cause: errorAppUpdate }
|
|
744
|
-
);
|
|
745
|
-
}
|
|
156
|
+
public isCmdInProgress(projectId: string): boolean {
|
|
157
|
+
return this.transactionManager.isOngoingTransactionForProjectID(projectId);
|
|
746
158
|
}
|
|
747
159
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
if (
|
|
755
|
-
appCfgUpdate?.updatedModels &&
|
|
756
|
-
Object.keys(appCfgUpdate.updatedModels).length
|
|
757
|
-
) {
|
|
758
|
-
// When there are model updates request signed URLs and wait to apply config changes
|
|
759
|
-
const { updatedModels } = appCfgUpdate;
|
|
760
|
-
|
|
761
|
-
logger.debug(
|
|
762
|
-
`Requesting presigned urls from cloud for model versions: ${JSON.stringify(
|
|
763
|
-
updatedModels
|
|
764
|
-
)}`
|
|
765
|
-
);
|
|
766
|
-
const modelsOnlyUrlsRequestPayload: SignedUrlsRequestPayload = {
|
|
767
|
-
modelsOnlyUrlsRequest: {
|
|
768
|
-
projectId,
|
|
769
|
-
models: updatedModels
|
|
770
|
-
}
|
|
771
|
-
};
|
|
772
|
-
const message = buildSignedUrlsRequestMessage(
|
|
773
|
-
this.clientId,
|
|
774
|
-
modelsOnlyUrlsRequestPayload,
|
|
775
|
-
txId
|
|
776
|
-
);
|
|
777
|
-
this.publisher.publishToCloud(message);
|
|
778
|
-
|
|
779
|
-
this.txnMgr.setAppCfgUpdateToTx(txId, update);
|
|
780
|
-
|
|
781
|
-
return false;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
if (appCfgUpdate) {
|
|
785
|
-
await this.atomicApplicationUpdate(async () => {
|
|
786
|
-
await pruneModels({
|
|
787
|
-
projectId,
|
|
788
|
-
appCfg: appCfgUpdate.newAppCfg
|
|
789
|
-
});
|
|
790
|
-
await updateAppCfg({
|
|
791
|
-
projectId,
|
|
792
|
-
newAppCfg: appCfgUpdate.newAppCfg
|
|
793
|
-
});
|
|
794
|
-
}, projectId);
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
if (envVarUpdate) {
|
|
798
|
-
await this.atomicApplicationUpdate(
|
|
799
|
-
async () =>
|
|
800
|
-
await this.shadowHandler.updateProjectEnvVars({
|
|
801
|
-
projectId,
|
|
802
|
-
envVars: envVarUpdate.envVars
|
|
803
|
-
}),
|
|
804
|
-
projectId,
|
|
805
|
-
true
|
|
806
|
-
);
|
|
807
|
-
}
|
|
808
|
-
return true;
|
|
809
|
-
};
|
|
810
|
-
|
|
811
|
-
private async handleProjectShadowMessage(topic: string, message: any) {
|
|
812
|
-
const shadowUpdates = await this.shadowHandler.handleProjectShadow({
|
|
813
|
-
topic,
|
|
814
|
-
payload: message,
|
|
815
|
-
clientToken: message.clientToken
|
|
816
|
-
});
|
|
817
|
-
if (shadowUpdates.length) {
|
|
818
|
-
const shadowUpdatePromises: Promise<void>[] = [];
|
|
819
|
-
for (const shadowUpdate of shadowUpdates) {
|
|
820
|
-
const projectId = shadowUpdate.projectId;
|
|
821
|
-
const txId = shadowUpdate.txId;
|
|
822
|
-
shadowUpdatePromises.push(
|
|
823
|
-
this.txnMgr
|
|
824
|
-
.runTransactionStep({
|
|
825
|
-
func: () =>
|
|
826
|
-
this.handleProjectShadowConfigUpdate(shadowUpdate, txId),
|
|
827
|
-
projectId,
|
|
828
|
-
txId,
|
|
829
|
-
start: true,
|
|
830
|
-
liveUpdatesPublishFn: async () =>
|
|
831
|
-
this.publisher.publishToClient(
|
|
832
|
-
buildToClientStatusResponseMessage(
|
|
833
|
-
this.clientId,
|
|
834
|
-
{ status: keyMirrors.statusResponse.in_progress },
|
|
835
|
-
txId
|
|
836
|
-
),
|
|
837
|
-
logger.silly
|
|
838
|
-
),
|
|
839
|
-
stepName: topic
|
|
840
|
-
})
|
|
841
|
-
.catch((e) => {
|
|
842
|
-
logger.error(
|
|
843
|
-
`There was an issue updating project shadow config for ${projectId}!\n${stringifyError(
|
|
844
|
-
e
|
|
845
|
-
)}`
|
|
846
|
-
);
|
|
847
|
-
})
|
|
848
|
-
);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
await Promise.all(shadowUpdatePromises);
|
|
852
|
-
}
|
|
160
|
+
public async stop() {
|
|
161
|
+
// This method is currently only used by the CLI, and shadow messages can be
|
|
162
|
+
// lost since we aren't waiting for responses so sleep for a short time to
|
|
163
|
+
// receive them
|
|
164
|
+
await sleep(1000);
|
|
165
|
+
this.connectionManager.disconnect();
|
|
853
166
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
logger.info(`Received secure tunnel update: ${JSON.stringify(payload)}`);
|
|
857
|
-
const state = getUpdateDeltaStateFromMessage(payload);
|
|
858
|
-
if (!state) {
|
|
859
|
-
logger.debug(`No state found in message: ${JSON.stringify(payload)}`);
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
862
|
-
const valid = validateSecureTunnelShadowUpdate(state);
|
|
863
|
-
if (!valid) {
|
|
864
|
-
logger.error(
|
|
865
|
-
`Error validating message: ${JSON.stringify(
|
|
866
|
-
{ payload, errors: validateSecureTunnelShadowUpdate.errors },
|
|
867
|
-
null,
|
|
868
|
-
2
|
|
869
|
-
)}`
|
|
870
|
-
);
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
|
-
const secureTunnelUpdate =
|
|
874
|
-
await this.secureTunnelHandler.syncShadowToDeviceState(payload);
|
|
875
|
-
await this.shadowHandler.updateSecureTunnelShadow(secureTunnelUpdate);
|
|
167
|
+
public async handleMessage(topic: string, message: any) {
|
|
168
|
+
this.connectionManager.dispatch(topic, message);
|
|
876
169
|
}
|
|
877
170
|
}
|
|
878
171
|
|