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