@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
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SecureTunnelMessageHandler = void 0;
|
|
4
|
+
const device_agent_schemas_1 = require("@alwaysai/device-agent-schemas");
|
|
5
|
+
const logger_1 = require("../util/logger");
|
|
6
|
+
const base_message_handler_1 = require("../cloud-connection/base-message-handler");
|
|
7
|
+
class SecureTunnelMessageHandler extends base_message_handler_1.BaseHandler {
|
|
8
|
+
getNotifyTopic() {
|
|
9
|
+
return `$aws/things/${this.clientId}/tunnels/notify`;
|
|
10
|
+
}
|
|
11
|
+
async handle(message, topic) {
|
|
12
|
+
const secureTunnelNotifyTopic = this.getNotifyTopic();
|
|
13
|
+
if (topic === secureTunnelNotifyTopic) {
|
|
14
|
+
await this.secureTunnelHandler.secureTunnelNotifyHandler(message);
|
|
15
|
+
}
|
|
16
|
+
else if (topic === this.shadowHandler.shadowTopics.secureTunnel.updateDelta) {
|
|
17
|
+
logger_1.logger.info(`Received secure tunnel update: ${JSON.stringify(message)}`);
|
|
18
|
+
await this.handleSecureTunnelMessage(message);
|
|
19
|
+
}
|
|
20
|
+
else if (topic === this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted) {
|
|
21
|
+
logger_1.logger.info(`Received secure tunnel deleteAccepted: ${message}`);
|
|
22
|
+
await this.secureTunnelHandler.destroy();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async handleSecureTunnelMessage(payload) {
|
|
26
|
+
logger_1.logger.info(`Received secure tunnel update: ${JSON.stringify(payload)}`);
|
|
27
|
+
const state = (0, device_agent_schemas_1.getUpdateDeltaStateFromMessage)(payload);
|
|
28
|
+
if (!state) {
|
|
29
|
+
logger_1.logger.debug(`No state found in message: ${JSON.stringify(payload)}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const valid = (0, device_agent_schemas_1.validateSecureTunnelShadowUpdate)(state);
|
|
33
|
+
if (!valid) {
|
|
34
|
+
logger_1.logger.error(`Error validating message: ${JSON.stringify({ payload, errors: device_agent_schemas_1.validateSecureTunnelShadowUpdate.errors }, null, 2)}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const secureTunnelUpdate = await this.secureTunnelHandler.syncShadowToDeviceState(payload);
|
|
38
|
+
await this.shadowHandler.updateSecureTunnelShadow(secureTunnelUpdate);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.SecureTunnelMessageHandler = SecureTunnelMessageHandler;
|
|
42
|
+
//# sourceMappingURL=secure-tunnel-message-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure-tunnel-message-handler.js","sourceRoot":"","sources":["../../src/secure-tunneling/secure-tunnel-message-handler.ts"],"names":[],"mappings":";;;AAAA,yEAGwC;AAExC,2CAAwC;AACxC,mFAAuE;AAEvE,MAAa,0BACX,SAAQ,kCAAW;IAGZ,cAAc;QACnB,OAAO,eAAe,IAAI,CAAC,QAAQ,iBAAiB,CAAC;IACvD,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,OAAY,EAAE,KAAa;QAC7C,MAAM,uBAAuB,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACtD,IAAI,KAAK,KAAK,uBAAuB,EAAE;YACrC,MAAM,IAAI,CAAC,mBAAmB,CAAC,yBAAyB,CAAC,OAAO,CAAC,CAAC;SACnE;aAAM,IACL,KAAK,KAAK,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,WAAW,EAClE;YACA,eAAM,CAAC,IAAI,CAAC,kCAAkC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACzE,MAAM,IAAI,CAAC,yBAAyB,CAAC,OAAO,CAAC,CAAC;SAC/C;aAAM,IACL,KAAK,KAAK,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,cAAc,EACrE;YACA,eAAM,CAAC,IAAI,CAAC,0CAA0C,OAAO,EAAE,CAAC,CAAC;YACjE,MAAM,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,CAAC;SAC1C;IACH,CAAC;IAEM,KAAK,CAAC,yBAAyB,CAAC,OAAY;QACjD,eAAM,CAAC,IAAI,CAAC,kCAAkC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzE,MAAM,KAAK,GAAG,IAAA,qDAA8B,EAAC,OAAO,CAAC,CAAC;QACtD,IAAI,CAAC,KAAK,EAAE;YACV,eAAM,CAAC,KAAK,CAAC,8BAA8B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACtE,OAAO;SACR;QACD,MAAM,KAAK,GAAG,IAAA,uDAAgC,EAAC,KAAK,CAAC,CAAC;QACtD,IAAI,CAAC,KAAK,EAAE;YACV,eAAM,CAAC,KAAK,CACV,6BAA6B,IAAI,CAAC,SAAS,CACzC,EAAE,OAAO,EAAE,MAAM,EAAE,uDAAgC,CAAC,MAAM,EAAE,EAC5D,IAAI,EACJ,CAAC,CACF,EAAE,CACJ,CAAC;YACF,OAAO;SACR;QACD,MAAM,kBAAkB,GACtB,MAAM,IAAI,CAAC,mBAAmB,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;QAClE,MAAM,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,kBAAkB,CAAC,CAAC;IACxE,CAAC;CACF;AA/CD,gEA+CC"}
|
|
@@ -3,6 +3,8 @@ export declare const installAppCliLeaf: import("@alwaysai/alwayscli/lib/types").
|
|
|
3
3
|
project: import("@alwaysai/alwayscli").CliInput<string, true>;
|
|
4
4
|
'release-hash': import("@alwaysai/alwayscli").CliInput<string | undefined, false>;
|
|
5
5
|
releaseHash: import("@alwaysai/alwayscli").CliInput<string | undefined, false>;
|
|
6
|
+
appCfg: import("@alwaysai/alwayscli").CliInput<string | undefined, false>;
|
|
7
|
+
envVars: import("@alwaysai/alwayscli").CliInput<string | undefined, false>;
|
|
6
8
|
}, import("@alwaysai/alwayscli").CliInput<undefined, false>>;
|
|
7
9
|
export declare const uninstallAppCliLeaf: import("@alwaysai/alwayscli/lib/types").CliLeaf<import("@alwaysai/alwayscli").CliInput<undefined, false>, {
|
|
8
10
|
project: import("@alwaysai/alwayscli").CliInput<string, true>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/subcommands/app/version.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,eAAe,yKAQ1B,CAAC;AAEH,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/subcommands/app/version.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,eAAe,yKAQ1B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;4DAkE5B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;4DA6B9B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;4DAc7B,CAAC"}
|
|
@@ -33,10 +33,20 @@ exports.installAppCliLeaf = (0, alwayscli_1.CliLeaf)({
|
|
|
33
33
|
description: 'Release Hash',
|
|
34
34
|
required: false,
|
|
35
35
|
hidden: true
|
|
36
|
+
}),
|
|
37
|
+
appCfg: (0, alwayscli_1.CliStringInput)({
|
|
38
|
+
description: 'Application Configuration',
|
|
39
|
+
required: false,
|
|
40
|
+
hidden: true
|
|
41
|
+
}),
|
|
42
|
+
envVars: (0, alwayscli_1.CliStringInput)({
|
|
43
|
+
description: 'Environment Variables configuration',
|
|
44
|
+
hidden: true,
|
|
45
|
+
required: false
|
|
36
46
|
})
|
|
37
47
|
},
|
|
38
48
|
async action(_, opts) {
|
|
39
|
-
const { project, releaseHash, 'release-hash': releaseHashNew } = opts;
|
|
49
|
+
const { project, releaseHash, 'release-hash': releaseHashNew, appCfg, envVars } = opts;
|
|
40
50
|
if (releaseHash) {
|
|
41
51
|
logger_1.logger.warn(`--releaseHash is deprecated and will be removed in a future release. Please switch to --release-hash`);
|
|
42
52
|
}
|
|
@@ -54,7 +64,9 @@ exports.installAppCliLeaf = (0, alwayscli_1.CliLeaf)({
|
|
|
54
64
|
payload: {
|
|
55
65
|
baseCommand: device_agent_schemas_1.keyMirrors.appVersionControl.install,
|
|
56
66
|
projectId: project,
|
|
57
|
-
appReleaseHash: releaseHashResolved
|
|
67
|
+
appReleaseHash: releaseHashResolved,
|
|
68
|
+
appCfg,
|
|
69
|
+
envVars
|
|
58
70
|
}
|
|
59
71
|
};
|
|
60
72
|
await deviceAgent.handleMessage(topic, message);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/subcommands/app/version.ts"],"names":[],"mappings":";;;AAAA,mDAA6E;AAC7E,yEAKwC;AACxC,mEAAwD;AACxD,wGAAkG;AAClG,oEAAoE;AACpE,4CAAqC;AACrC,8CAA2C;AAE9B,QAAA,eAAe,GAAG,IAAA,mBAAO,EAAC;IACrC,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,yBAAyB;IACtC,WAAW,EAAE,EAAE;IACf,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI;QAClB,MAAM,IAAI,GAAG,MAAM,IAAA,8BAAe,GAAE,CAAC,OAAO,EAAE,CAAC;QAC/C,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;CACF,CAAC,CAAC;AAEU,QAAA,iBAAiB,GAAG,IAAA,mBAAO,EAAC;IACvC,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,wCAAwC;IACrD,WAAW,EAAE;QACX,OAAO,EAAE,IAAA,0BAAc,EAAC;YACtB,WAAW,EAAE,YAAY;YACzB,QAAQ,EAAE,IAAI;SACf,CAAC;QACF,cAAc,EAAE,IAAA,0BAAc,EAAC;YAC7B,WAAW,EAAE,cAAc;YAC3B,QAAQ,EAAE,KAAK;SAChB,CAAC;QACF,WAAW,EAAE,IAAA,0BAAc,EAAC;YAC1B,WAAW,EAAE,cAAc;YAC3B,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,IAAI;SACb,CAAC;KACH;IACD,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI;QAClB,MAAM,
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/subcommands/app/version.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alwaysai/device-agent",
|
|
3
3
|
"description": "The alwaysAI Device Agent",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.1",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
7
7
|
"publishConfig": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@alwaysai/alwayscli": "0.3.3",
|
|
38
38
|
"@alwaysai/app-configuration-schemas": "0.2.0",
|
|
39
39
|
"@alwaysai/config-nodejs": "0.3.3",
|
|
40
|
-
"@alwaysai/device-agent-schemas": "3.
|
|
40
|
+
"@alwaysai/device-agent-schemas": "3.3.0",
|
|
41
41
|
"@carnesen/coded-error": "0.4.0",
|
|
42
42
|
"ajv": "8.11.0",
|
|
43
43
|
"alwaysai": "2.6.1",
|
|
@@ -6,9 +6,11 @@ import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
|
6
6
|
import { isAppStarted, restartApp } from './status';
|
|
7
7
|
import { EnvVars } from '@alwaysai/device-agent-schemas';
|
|
8
8
|
|
|
9
|
-
export async function setEnv(props: {
|
|
9
|
+
export async function setEnv(props: {
|
|
10
|
+
projectId: string;
|
|
11
|
+
envVars: EnvVars;
|
|
12
|
+
}): Promise<void> {
|
|
10
13
|
const { projectId, envVars } = props;
|
|
11
|
-
await requireAppReady({ projectId });
|
|
12
14
|
const appReleaseHash = await AgentConfigFile().getAppVersion({
|
|
13
15
|
projectId
|
|
14
16
|
});
|
|
@@ -16,6 +18,33 @@ export async function setEnv(props: { projectId: string; envVars: EnvVars }) {
|
|
|
16
18
|
projectId,
|
|
17
19
|
version: appReleaseHash
|
|
18
20
|
});
|
|
21
|
+
await setEnvInternal({ projectId, envVars });
|
|
22
|
+
|
|
23
|
+
const appDir = getAppDir(projectId);
|
|
24
|
+
await buildApp({ appDir });
|
|
25
|
+
|
|
26
|
+
await AgentConfigFile().setAppInstalled({
|
|
27
|
+
projectId,
|
|
28
|
+
version: appReleaseHash
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (await isAppStarted({ projectId })) {
|
|
32
|
+
await restartApp({ projectId });
|
|
33
|
+
}
|
|
34
|
+
logger.info(
|
|
35
|
+
`Updated environment variables for ${projectId}: ${JSON.stringify(
|
|
36
|
+
envVars,
|
|
37
|
+
null,
|
|
38
|
+
2
|
|
39
|
+
)}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function setEnvInternal(props: {
|
|
44
|
+
projectId: string;
|
|
45
|
+
envVars: EnvVars;
|
|
46
|
+
}): Promise<void> {
|
|
47
|
+
const { projectId, envVars } = props;
|
|
19
48
|
|
|
20
49
|
const composeParsed = await readDockerCompose({ projectId });
|
|
21
50
|
if (!('services' in composeParsed)) {
|
|
@@ -45,25 +74,6 @@ export async function setEnv(props: { projectId: string; envVars: EnvVars }) {
|
|
|
45
74
|
});
|
|
46
75
|
|
|
47
76
|
await writeDockerCompose({ projectId, dockerCompose: composeParsed });
|
|
48
|
-
|
|
49
|
-
const appDir = getAppDir(projectId);
|
|
50
|
-
await buildApp({ appDir });
|
|
51
|
-
|
|
52
|
-
await AgentConfigFile().setAppInstalled({
|
|
53
|
-
projectId,
|
|
54
|
-
version: appReleaseHash
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (await isAppStarted({ projectId })) {
|
|
58
|
-
await restartApp({ projectId });
|
|
59
|
-
}
|
|
60
|
-
logger.info(
|
|
61
|
-
`Updated environment variables for ${projectId}: ${JSON.stringify(
|
|
62
|
-
envVars,
|
|
63
|
-
null,
|
|
64
|
-
2
|
|
65
|
-
)}`
|
|
66
|
-
);
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
export function convertStringEnvsToKeyVal(stringEnvs: string[]) {
|
|
@@ -4,7 +4,7 @@ import * as path from 'path';
|
|
|
4
4
|
import { buildDockerImage } from 'alwaysai/lib/util/docker';
|
|
5
5
|
import { JsSpawner, Spawner, stringifyError } from 'alwaysai/lib/util';
|
|
6
6
|
import { getAppDir, buildApp } from './utils';
|
|
7
|
-
import { AppDetails } from '@alwaysai/device-agent-schemas';
|
|
7
|
+
import { AppDetails, EnvVars } from '@alwaysai/device-agent-schemas';
|
|
8
8
|
import { BACKUP_EXT } from './backup';
|
|
9
9
|
import { startApp, stopApp, isAppStarted } from './status';
|
|
10
10
|
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
@@ -22,7 +22,7 @@ import { DOCKER_IMAGE_ID_INITIAL_VALUE } from 'alwaysai/lib/constants';
|
|
|
22
22
|
import { runInDir } from '../util/run-in-dir';
|
|
23
23
|
import { installModelsWithPresignedURLs } from './models';
|
|
24
24
|
import { logger } from '../util/logger';
|
|
25
|
-
import { updateAppCfgFile } from './config';
|
|
25
|
+
import { updateAppCfgFile, writeAppCfgFile } from './config';
|
|
26
26
|
import {
|
|
27
27
|
LOCAL_CONNECTION_HOST,
|
|
28
28
|
LOCAL_CONNECTION_PORT,
|
|
@@ -30,6 +30,8 @@ import {
|
|
|
30
30
|
} from '../local-connection/constants';
|
|
31
31
|
import { DOCKERFILE, PYTHON_REQUIREMENTS_FILE_NAME } from 'alwaysai/lib/paths';
|
|
32
32
|
import { downloadToFile } from '../util/file';
|
|
33
|
+
import { setEnvInternal } from './environment-variables';
|
|
34
|
+
import { AppConfig } from '@alwaysai/app-configuration-schemas';
|
|
33
35
|
|
|
34
36
|
type SignedUrlPayloadType = {
|
|
35
37
|
appInstallPayload: {
|
|
@@ -56,8 +58,11 @@ export async function installApp(props: {
|
|
|
56
58
|
projectId: string;
|
|
57
59
|
appReleaseHash: string;
|
|
58
60
|
signedUrlsPayload: SignedUrlPayloadType;
|
|
61
|
+
appCfg?: AppConfig;
|
|
62
|
+
envVars?: EnvVars;
|
|
59
63
|
}): Promise<void> {
|
|
60
|
-
const { projectId, appReleaseHash, signedUrlsPayload } =
|
|
64
|
+
const { projectId, appReleaseHash, signedUrlsPayload, appCfg, envVars } =
|
|
65
|
+
props;
|
|
61
66
|
logger.info(`Installing ${projectId}:${appReleaseHash}`);
|
|
62
67
|
|
|
63
68
|
const appDir = getAppDir(projectId);
|
|
@@ -127,6 +132,22 @@ export async function installApp(props: {
|
|
|
127
132
|
path.join(appDir, 'models')
|
|
128
133
|
);
|
|
129
134
|
|
|
135
|
+
// See if there's appCfg or envVars in the transaction, and if so, apply it
|
|
136
|
+
|
|
137
|
+
if (appCfg) {
|
|
138
|
+
logger.debug(`applying appCfg: ${JSON.stringify(appCfg)}`);
|
|
139
|
+
await writeAppCfgFile({ projectId, appCfg });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (envVars) {
|
|
143
|
+
logger.debug(`applying envVarUpdate: ${JSON.stringify(envVars)}`);
|
|
144
|
+
try {
|
|
145
|
+
await setEnvInternal({ projectId, envVars });
|
|
146
|
+
} catch (error) {
|
|
147
|
+
logger.error(`error is : ${error}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
130
151
|
await installAppBuildReqs({ appDir });
|
|
131
152
|
await buildApp({ appDir });
|
|
132
153
|
|
|
@@ -74,4 +74,17 @@ export async function buildApp(props: { appDir: string }) {
|
|
|
74
74
|
`Failed to build application! stdout=${buildOut.out} stderr=${buildOut.err}`
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
|
+
|
|
78
|
+
// Prune unused Docker resources after a successful build
|
|
79
|
+
logger.debug('Pruning docker system');
|
|
80
|
+
try {
|
|
81
|
+
const spawner = JsSpawner();
|
|
82
|
+
const pruneOut = await spawner.run({
|
|
83
|
+
exe: 'docker',
|
|
84
|
+
args: ['system', 'prune', '-f']
|
|
85
|
+
});
|
|
86
|
+
logger.debug(pruneOut);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
logger.error(`Stderr occurred when pruning docker system: ${error}`);
|
|
89
|
+
}
|
|
77
90
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { logger, stringifyError } from 'alwaysai/lib/util';
|
|
2
|
+
import { uninstallApp, rollbackApp } from '../application-control';
|
|
3
|
+
import { createAppBackup } from '../application-control/backup';
|
|
4
|
+
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
5
|
+
import { SecureTunnelHandlerSingleton } from '../secure-tunneling/secure-tunneling';
|
|
6
|
+
import AaiError from '../util/aai-error';
|
|
7
|
+
import { LiveUpdatesHandler } from './live-updates-handler';
|
|
8
|
+
import { Publisher } from './publisher';
|
|
9
|
+
import { ShadowHandler } from './shadow-handler';
|
|
10
|
+
import {
|
|
11
|
+
TransactionManager,
|
|
12
|
+
ErrorFunction,
|
|
13
|
+
SuccessFunction
|
|
14
|
+
} from './transaction-manager';
|
|
15
|
+
|
|
16
|
+
export interface HandlerContext {
|
|
17
|
+
clientId: string;
|
|
18
|
+
txnMgr: TransactionManager;
|
|
19
|
+
publisher: Publisher;
|
|
20
|
+
shadowHandler: ShadowHandler;
|
|
21
|
+
liveUpdatesHandler: LiveUpdatesHandler;
|
|
22
|
+
secureTunnelHandler: SecureTunnelHandlerSingleton;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export abstract class BaseHandler {
|
|
26
|
+
protected clientId: string;
|
|
27
|
+
protected txnMgr: TransactionManager;
|
|
28
|
+
protected publisher: Publisher;
|
|
29
|
+
protected shadowHandler: ShadowHandler;
|
|
30
|
+
protected liveUpdatesHandler: LiveUpdatesHandler;
|
|
31
|
+
protected secureTunnelHandler: SecureTunnelHandlerSingleton;
|
|
32
|
+
protected readonly errorFn?: ErrorFunction;
|
|
33
|
+
protected readonly successFn?: SuccessFunction;
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
context: HandlerContext,
|
|
37
|
+
errorFn?: ErrorFunction,
|
|
38
|
+
successFn?: SuccessFunction
|
|
39
|
+
) {
|
|
40
|
+
this.clientId = context.clientId;
|
|
41
|
+
this.txnMgr = context.txnMgr;
|
|
42
|
+
this.publisher = context.publisher;
|
|
43
|
+
this.shadowHandler = context.shadowHandler;
|
|
44
|
+
this.liveUpdatesHandler = context.liveUpdatesHandler;
|
|
45
|
+
this.secureTunnelHandler = context.secureTunnelHandler;
|
|
46
|
+
this.errorFn = errorFn;
|
|
47
|
+
this.successFn = successFn;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
protected async atomicApplicationUninstall(projectId: string) {
|
|
51
|
+
try {
|
|
52
|
+
await uninstallApp({ projectId });
|
|
53
|
+
this.shadowHandler.clearProjectShadow(projectId);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
logger.error(`Failed to uninstall ${projectId}!\n${stringifyError(e)}`);
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected async atomicApplicationUpdate<F extends () => any>(
|
|
61
|
+
func: F,
|
|
62
|
+
projectId: string,
|
|
63
|
+
skipUpdateShadow?: boolean
|
|
64
|
+
): Promise<ReturnType<F>> {
|
|
65
|
+
if (await AgentConfigFile().isAppPresent({ projectId })) {
|
|
66
|
+
// Reject the application update if app is present but not ready
|
|
67
|
+
if (!(await AgentConfigFile().isAppReady({ projectId }))) {
|
|
68
|
+
throw new Error('Application already has installation in progress!');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Try to create a backup, so that there is one available if something goes wrong in the next try:catch.
|
|
72
|
+
try {
|
|
73
|
+
await createAppBackup({ projectId });
|
|
74
|
+
} catch (e) {
|
|
75
|
+
logger.error(
|
|
76
|
+
`Could not create a backup for the project: ${projectId}!\n${stringifyError(
|
|
77
|
+
e
|
|
78
|
+
)}`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const out: ReturnType<F> = await func();
|
|
85
|
+
if (!skipUpdateShadow)
|
|
86
|
+
await this.shadowHandler.updateProjectShadow(projectId);
|
|
87
|
+
return out;
|
|
88
|
+
} catch (errorAppUpdate) {
|
|
89
|
+
logger.error(
|
|
90
|
+
`Failed to update ${projectId}!\n${stringifyError(errorAppUpdate)}`
|
|
91
|
+
);
|
|
92
|
+
// If something goes wrong, first try to rollback
|
|
93
|
+
try {
|
|
94
|
+
await rollbackApp({ projectId });
|
|
95
|
+
} catch (errorRollbackApp) {
|
|
96
|
+
logger.error(
|
|
97
|
+
`Application rollback failed for ${projectId}!\n${stringifyError(
|
|
98
|
+
errorRollbackApp
|
|
99
|
+
)}`
|
|
100
|
+
);
|
|
101
|
+
// and if that fails, uninstall the app as a last resort.
|
|
102
|
+
try {
|
|
103
|
+
await this.atomicApplicationUninstall(projectId);
|
|
104
|
+
} catch {
|
|
105
|
+
// atomicApplicationUninstall logs failure, so there's nothing to do here.
|
|
106
|
+
}
|
|
107
|
+
throw new AaiError(
|
|
108
|
+
'Application update and rollback failed, uninstalled the application!',
|
|
109
|
+
{ cause: errorAppUpdate }
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
throw new AaiError(
|
|
113
|
+
'Application update failed, rolled the application back!',
|
|
114
|
+
{ cause: errorAppUpdate }
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import * as awsIot from 'aws-iot-device-sdk';
|
|
2
|
+
import {
|
|
3
|
+
DEVICE_CERTIFICATE_FILE_PATH,
|
|
4
|
+
DEVICE_PRIVATE_KEY_FILE_PATH
|
|
5
|
+
} from 'alwaysai/lib/infrastructure';
|
|
6
|
+
import { AWS_ROOT_CERTIFICATE_FILE_PATH } from '../util/directories';
|
|
7
|
+
import { stringifyError } from 'alwaysai/lib/util';
|
|
8
|
+
import { logger } from '../util/logger';
|
|
9
|
+
import { promisify } from 'util';
|
|
10
|
+
import { exec } from 'child_process';
|
|
11
|
+
import { MessageDispatcher, MessageHandler } from './message-dispatcher';
|
|
12
|
+
|
|
13
|
+
const exec_promise = promisify(exec);
|
|
14
|
+
|
|
15
|
+
export class ConnectionManager extends MessageDispatcher<any> {
|
|
16
|
+
private clientId: string;
|
|
17
|
+
private host: string;
|
|
18
|
+
private port: number;
|
|
19
|
+
private device = awsIot.device;
|
|
20
|
+
private subscribedTopics: Set<string> = new Set();
|
|
21
|
+
|
|
22
|
+
constructor(clientId: string, host: string, port: number) {
|
|
23
|
+
super();
|
|
24
|
+
this.clientId = clientId;
|
|
25
|
+
this.host = host;
|
|
26
|
+
this.port = port;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public getIoTDevice() {
|
|
30
|
+
return this.device;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public setupConnection(): void {
|
|
34
|
+
this.connect();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public connect(): void {
|
|
38
|
+
try {
|
|
39
|
+
this.device = awsIot.device({
|
|
40
|
+
keyPath: DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
41
|
+
certPath: DEVICE_CERTIFICATE_FILE_PATH,
|
|
42
|
+
caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
43
|
+
clientId: this.clientId,
|
|
44
|
+
host: this.host,
|
|
45
|
+
port: this.port,
|
|
46
|
+
keepalive: 10 // time before re-connect attempt on dropped connection, default is 400 seconds
|
|
47
|
+
});
|
|
48
|
+
this.setupHandlers();
|
|
49
|
+
} catch (error) {
|
|
50
|
+
logger.error(`Error connecting to cloud!\n${stringifyError(error)}`);
|
|
51
|
+
this.handleReconnection();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public registerHandler(topic: string, handler: MessageHandler) {
|
|
56
|
+
super.registerHandler(topic, handler);
|
|
57
|
+
this.subscribe(topic);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public disconnect(): void {
|
|
61
|
+
if (this.device) {
|
|
62
|
+
this.device.end();
|
|
63
|
+
logger.debug(`Device Agent has been disconnected from the AWS IoT Core.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public subscribe(topic: string): void {
|
|
68
|
+
if (this.device && !this.subscribedTopics.has(topic)) {
|
|
69
|
+
this.device.subscribe(topic);
|
|
70
|
+
this.subscribedTopics.add(topic);
|
|
71
|
+
logger.debug(`Subscribed to topic: ${topic}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public unsubscribe(topic: string): void {
|
|
76
|
+
if (this.device && this.subscribedTopics.has(topic)) {
|
|
77
|
+
this.device.unsubscribe(topic);
|
|
78
|
+
this.subscribedTopics.delete(topic);
|
|
79
|
+
logger.debug(`Unsubscribed from topic: ${topic}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public isConnected(): boolean {
|
|
84
|
+
return this.device ? this.device.connected : false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private handleReconnection() {
|
|
88
|
+
logger.debug(`Attempting to reconnect to AWS IoT Core...`);
|
|
89
|
+
setTimeout(() => this.connect(), 5000); // try in 5 seconds.
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private setupHandlers(): void {
|
|
93
|
+
this.device.on('connect', (connack: any) => {
|
|
94
|
+
logger.info('Device Agent has connected to the cloud.');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.device.on('disconnect', () => {
|
|
98
|
+
logger.warn('Device Agent has been disconnected from the cloud');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
this.device.on('reconnect', () => {
|
|
102
|
+
logger.info(
|
|
103
|
+
`Device Agent attempting to re-connect ${new Date().toLocaleString()}`
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
this.device.on('error', function (e) {
|
|
108
|
+
logger.error(
|
|
109
|
+
`Error connecting to the AWS IoT Core!\n${stringifyError(e)}`
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
this.device.on('close', () => {
|
|
114
|
+
logger.warn('Device Agent AWS IoT Core connection closed.');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.device.on('offline', () => {
|
|
118
|
+
logger.warn(`Device Agent is offline ${new Date().toLocaleString()}`);
|
|
119
|
+
void this.logConnectionInfo();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
this.device.on('message', async (topic: string, payload: string) => {
|
|
123
|
+
logger.debug(
|
|
124
|
+
`Message received on topic: ${topic}:\n${payload.toString()}`
|
|
125
|
+
);
|
|
126
|
+
try {
|
|
127
|
+
const jsonPacket = JSON.parse(payload);
|
|
128
|
+
this.dispatch(topic, jsonPacket);
|
|
129
|
+
} catch (e) {
|
|
130
|
+
logger.error(`Error parsing message!\n${stringifyError(e)}`);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private async logConnectionInfo() {
|
|
136
|
+
try {
|
|
137
|
+
/**
|
|
138
|
+
* We're using the 'netcat' or 'nc' command to test the connection to the IoT Core endpoint.
|
|
139
|
+
* This command doesn't always exit (see below), so
|
|
140
|
+
* we use timeout to break out of the prompt
|
|
141
|
+
* and catch the resulting error/parse the resulting stderr
|
|
142
|
+
*
|
|
143
|
+
* Sample command for current host and port:
|
|
144
|
+
* nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 8883
|
|
145
|
+
*
|
|
146
|
+
* Sample output when port is not blocked and host is reachable:
|
|
147
|
+
* $ nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 443
|
|
148
|
+
* Connection to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 443 port [tcp/https] succeeded!
|
|
149
|
+
*
|
|
150
|
+
*
|
|
151
|
+
* Sample output when port is blocked (will repeatedly try until ctrl-C out):
|
|
152
|
+
* $ nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 8883
|
|
153
|
+
* nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
|
|
154
|
+
* nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
|
|
155
|
+
* nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
|
|
156
|
+
* ^C
|
|
157
|
+
*
|
|
158
|
+
*
|
|
159
|
+
* Sample command/output when the port isn't enable on that host:
|
|
160
|
+
* $ nc -zv -w 1 localhost 8883
|
|
161
|
+
* nc: connect to localhost port 8883 (tcp) failed: Connection refused
|
|
162
|
+
*/
|
|
163
|
+
await exec_promise(`nc -zv -w 1 ${this.host} ${this.port}`, {
|
|
164
|
+
timeout: 2000
|
|
165
|
+
});
|
|
166
|
+
} catch (err) {
|
|
167
|
+
const output = JSON.stringify(err['stderr']);
|
|
168
|
+
if (output.indexOf('not known') !== -1) {
|
|
169
|
+
logger.warn(
|
|
170
|
+
'Iot Core endpoint appears to be unreachable, internet connection may be unstable or the host may be down.'
|
|
171
|
+
);
|
|
172
|
+
} else if (output.indexOf('timed out') !== -1) {
|
|
173
|
+
logger.warn(
|
|
174
|
+
`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.`
|
|
175
|
+
);
|
|
176
|
+
} else if (output.indexOf('refused') !== -1) {
|
|
177
|
+
logger.warn(
|
|
178
|
+
`The connection was refused, likely ${this.host} is not running a service on ${this.port}.`
|
|
179
|
+
);
|
|
180
|
+
} else {
|
|
181
|
+
logger.warn(
|
|
182
|
+
`Output from checking connection to ${this.host} on ${this.port}: ${output}`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|