@alwaysai/device-agent 0.0.20 → 0.1.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/config.d.ts +0 -1
- package/lib/application-control/config.d.ts.map +1 -1
- package/lib/application-control/config.js +15 -29
- package/lib/application-control/config.js.map +1 -1
- package/lib/application-control/environment-variables.d.ts +7 -3
- package/lib/application-control/environment-variables.d.ts.map +1 -1
- package/lib/application-control/environment-variables.js +71 -35
- package/lib/application-control/environment-variables.js.map +1 -1
- package/lib/application-control/environment-variables.test.d.ts +2 -0
- package/lib/application-control/environment-variables.test.d.ts.map +1 -0
- package/lib/application-control/environment-variables.test.js +163 -0
- package/lib/application-control/environment-variables.test.js.map +1 -0
- package/lib/application-control/index.d.ts +3 -3
- package/lib/application-control/index.d.ts.map +1 -1
- package/lib/application-control/index.js +1 -3
- package/lib/application-control/index.js.map +1 -1
- package/lib/application-control/models.d.ts +0 -1
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +12 -26
- package/lib/application-control/models.js.map +1 -1
- package/lib/application-control/status.d.ts +3 -0
- package/lib/application-control/status.d.ts.map +1 -1
- package/lib/application-control/status.js +19 -1
- package/lib/application-control/status.js.map +1 -1
- package/lib/application-control/utils.d.ts.map +1 -1
- package/lib/application-control/utils.js +2 -2
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +6 -3
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +201 -151
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts +3 -0
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +23 -7
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.test.d.ts +2 -0
- package/lib/cloud-connection/live-updates-handler.test.d.ts.map +1 -0
- package/lib/cloud-connection/live-updates-handler.test.js +57 -0
- package/lib/cloud-connection/live-updates-handler.test.js.map +1 -0
- package/lib/cloud-connection/shadow-handler.d.ts +11 -3
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +22 -7
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.test.js +313 -228
- package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
- package/lib/cloud-connection/shadow.js +1 -1
- package/lib/cloud-connection/shadow.js.map +1 -1
- package/lib/environment.d.ts +1 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js +2 -1
- package/lib/environment.js.map +1 -1
- package/lib/infrastructure/agent-config.d.ts +3 -1
- package/lib/infrastructure/agent-config.d.ts.map +1 -1
- package/lib/subcommands/app/env-vars.d.ts +1 -1
- package/lib/subcommands/app/env-vars.d.ts.map +1 -1
- package/lib/subcommands/app/env-vars.js +32 -5
- package/lib/subcommands/app/env-vars.js.map +1 -1
- package/lib/subcommands/app/index.d.ts.map +1 -1
- package/lib/subcommands/app/index.js +4 -1
- package/lib/subcommands/app/index.js.map +1 -1
- package/lib/subcommands/app/models.d.ts.map +1 -1
- package/lib/subcommands/app/models.js +6 -1
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/app/shadow.d.ts +7 -0
- package/lib/subcommands/app/shadow.d.ts.map +1 -0
- package/lib/subcommands/app/shadow.js +48 -0
- package/lib/subcommands/app/shadow.js.map +1 -0
- package/lib/subcommands/app/version.js +2 -2
- package/lib/subcommands/app/version.js.map +1 -1
- package/lib/util/cloud-mode-ready.d.ts +2 -0
- package/lib/util/cloud-mode-ready.d.ts.map +1 -0
- package/lib/util/cloud-mode-ready.js +22 -0
- package/lib/util/cloud-mode-ready.js.map +1 -0
- package/package.json +1 -1
- package/readme.md +140 -22
- package/src/application-control/config.ts +30 -31
- package/src/application-control/environment-variables.test.ts +171 -0
- package/src/application-control/environment-variables.ts +102 -43
- package/src/application-control/index.ts +3 -9
- package/src/application-control/models.ts +14 -29
- package/src/application-control/status.ts +20 -0
- package/src/application-control/utils.ts +4 -2
- package/src/cloud-connection/device-agent-cloud-connection.ts +220 -155
- package/src/cloud-connection/live-updates-handler.test.ts +68 -0
- package/src/cloud-connection/live-updates-handler.ts +30 -7
- package/src/cloud-connection/shadow-handler.test.ts +329 -239
- package/src/cloud-connection/shadow-handler.ts +38 -12
- package/src/cloud-connection/shadow.ts +1 -1
- package/src/environment.ts +2 -0
- package/src/infrastructure/agent-config.ts +1 -1
- package/src/subcommands/app/env-vars.ts +38 -8
- package/src/subcommands/app/index.ts +4 -1
- package/src/subcommands/app/models.ts +10 -1
- package/src/subcommands/app/shadow.ts +48 -0
- package/src/subcommands/app/version.ts +2 -2
- package/src/util/cloud-mode-ready.ts +23 -0
package/readme.md
CHANGED
|
@@ -36,7 +36,7 @@ and add the following line to the end of the file:
|
|
|
36
36
|
|
|
37
37
|
On the target device, run:
|
|
38
38
|
|
|
39
|
-
```
|
|
39
|
+
```bash
|
|
40
40
|
$ curl -fsSL https://artifacts.alwaysai.co/device-agent/install-device-agent.sh | sudo -E bash -
|
|
41
41
|
```
|
|
42
42
|
|
|
@@ -52,7 +52,7 @@ Provisioning the device performs the following:
|
|
|
52
52
|
|
|
53
53
|
Run the following command on the target device to provision it:
|
|
54
54
|
|
|
55
|
-
```
|
|
55
|
+
```bash
|
|
56
56
|
$ curl -fsSL https://artifacts.alwaysai.co/device-agent/provision.sh | bash -s -- --email <email> --password <password> [--device-name <device_name>]
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -63,6 +63,8 @@ Where:
|
|
|
63
63
|
devices page of the alwaysAI Dashboard. If a device name is not provided, one
|
|
64
64
|
will be generated for you and logged to the console for reference.
|
|
65
65
|
|
|
66
|
+
**Important note**: If your password contains one or more special characters and you receive an error message that your password does not match your username but you are sure that it is correctly entered, please try enclosing your password with double quotes and re-running the provisioning command. You can also reset your password at https://console.alwaysai.co/dashboard by logging out, navigating to the sign in page, and clicking '`Forgot Password?`'.
|
|
67
|
+
|
|
66
68
|
Confirm the Device Agent is running with the following command:
|
|
67
69
|
|
|
68
70
|
```bash
|
|
@@ -74,7 +76,7 @@ $ pm2 list
|
|
|
74
76
|
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
|
|
75
77
|
```
|
|
76
78
|
|
|
77
|
-
To restart the Device Agent run the following command:
|
|
79
|
+
To restart and update the Device Agent run the following command:
|
|
78
80
|
|
|
79
81
|
```bash
|
|
80
82
|
$ pm2 restart aai-agent
|
|
@@ -91,7 +93,7 @@ Use --update-env to update environment variables
|
|
|
91
93
|
If you'd like to only provision the device, but not start the Device Agent in
|
|
92
94
|
the background (skip step 3), run with the `--provision-only` flag:
|
|
93
95
|
|
|
94
|
-
```
|
|
96
|
+
```bash
|
|
95
97
|
$ curl -fsSL https://artifacts.alwaysai.co/device-agent/provision.sh | bash -s -- --email <email> --password <password> [--device-name <device_name>] --provision-only
|
|
96
98
|
```
|
|
97
99
|
|
|
@@ -116,11 +118,129 @@ project page of the alwaysAI Dashboard as well!
|
|
|
116
118
|
|
|
117
119
|
Now you can deploy to your device from the alwaysAI Dashboard.
|
|
118
120
|
|
|
121
|
+
## Enable Analytics through the alwaysAI Device Agent
|
|
122
|
+
|
|
123
|
+
### Configure the Device Agent
|
|
124
|
+
You can send information from your device to the alwaysAI cloud securely using
|
|
125
|
+
the Device Agent. These instructions assume you have provisioned your device
|
|
126
|
+
using the default script parameters and have the Device agent running (i.e. the
|
|
127
|
+
provisioning script was not run with the `--provision-only` flag set).
|
|
128
|
+
|
|
129
|
+
First, the Device Agent must be configured to enable Analytics Pass-through support. Confirm that the `ALWAYSAI_ANALYTICS_PASSTHROUGH` environment variable is set:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
$ pm2 env 0 | grep ALWAYSAI
|
|
133
|
+
ALWAYSAI_ANALYTICS_PASSTHROUGH: 1
|
|
134
|
+
ALWAYSAI_LOG_TO_CONSOLE:
|
|
135
|
+
ALWAYSAI_LOG_LEVEL: debug
|
|
136
|
+
ALWAYSAI_DEVICE_AGENT_MODE: cloud
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Then confirm the RabbitMQ container is up and running:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
$ docker ps
|
|
143
|
+
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
|
144
|
+
596157124a4b rabbitmq:3.11 "docker-entrypoint.s…" 32 minutes ago Up 21 minutes 4369/tcp, 5671/tcp, 15691-15692/tcp, 25672/tcp, 0.0.0.0:5672->5672/tcp alwaysAIRabbitMQContainer
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Configure the Application
|
|
148
|
+
|
|
149
|
+
From the application side, the `ALWAYSAI_CONNECT_TO_DEVICE_AGENT` environment
|
|
150
|
+
variable must be set. There are a few options:
|
|
151
|
+
|
|
152
|
+
#### In the app source
|
|
153
|
+
|
|
154
|
+
In your `app.py` file, make sure the top of your import statements looks like
|
|
155
|
+
this:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
import os
|
|
159
|
+
os.environ['ALWAYSAI_CONNECT_TO_DEVICE_AGENT']='1'
|
|
160
|
+
import edgeiq
|
|
161
|
+
```
|
|
162
|
+
You can import any other modules after `edgeiq`, but the other orders must be
|
|
163
|
+
maintained.
|
|
164
|
+
|
|
165
|
+
#### In the Dockerfile
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
ENV ALWAYSAI_CONNECT_TO_DEVICE_AGENT=1
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### In a docker-compose.yaml
|
|
172
|
+
|
|
173
|
+
Add the following section to the service for your app:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
environment:
|
|
177
|
+
- ALWAYSAI_CONNECT_TO_DEVICE_AGENT=1
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Enable Publishing to Cloud
|
|
181
|
+
|
|
182
|
+
To enable cloud publishing for your application you can either run
|
|
183
|
+
`aai app enable-cloud-publish`, or update your `alwaysai.app.json` by adding the
|
|
184
|
+
following component in addition to any `models` or `scripts` components:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
"analytics: {
|
|
188
|
+
"enable_cloud_publish": true
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
So, a valid `alwaysai.app.json` for publishing analytics might look like this:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"scripts": {
|
|
197
|
+
"start": "python app.py"
|
|
198
|
+
},
|
|
199
|
+
"models": {
|
|
200
|
+
"alwaysai/mobilenet_ssd": 4
|
|
201
|
+
},
|
|
202
|
+
"analytics": {
|
|
203
|
+
"enable_cloud_publish": true
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Finally, add a command to publish analytics to your `app.py`. Whenever this
|
|
209
|
+
command is used, it will publish the contents of the analytics message to the
|
|
210
|
+
cloud -- you can choose to call this every frame, or once every event
|
|
211
|
+
occurrence, however your app is designed. Each core computer vision service has
|
|
212
|
+
it's own `publish_analytics` method, which is called on the instance of the
|
|
213
|
+
class. Or, you can published a JSON-serializable message with
|
|
214
|
+
`edgeiq.publish_analytics()`. For instance, to publish object detection results
|
|
215
|
+
you can use:
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
results = obj_detect.detect_objects(frame, confidence_level=.5)
|
|
219
|
+
try:
|
|
220
|
+
obj_detect.publish_analytics(results, tag=frame_count)
|
|
221
|
+
except edgeiq.PublishError as e:
|
|
222
|
+
print(e)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Finally, make sure to save all of your changes, and publish your application with
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
$ aai app publish
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
You can test that analytics are being viewed with `wscat`, using your application's project ID and a secure API key:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
$ wscat -c "wss://analytics.alwaysai.co?projectId=[PROJECT_ID]&apiKey=[API_KEY]"
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Please contact the alwaysAI team if you need a secure API key.
|
|
238
|
+
|
|
119
239
|
## The alwaysAI Device Agent Command Line interface
|
|
120
240
|
|
|
121
241
|
The Device Agent can also be used directly on the device with it's command line interface.
|
|
122
242
|
|
|
123
|
-
```
|
|
243
|
+
```bash
|
|
124
244
|
$ aai-agent --help
|
|
125
245
|
Usage: aai-agent <subcommand> ...
|
|
126
246
|
|
|
@@ -140,8 +260,6 @@ Subcommands:
|
|
|
140
260
|
app show-models : Show the application models
|
|
141
261
|
app add-model : Add a model to an alwaysAI app
|
|
142
262
|
app remove-model : Remove a model from an alwaysAI app
|
|
143
|
-
app replace-models : Replace all models of an alwaysAI app with new models
|
|
144
|
-
app update-models : Update all models for an alwaysAI app
|
|
145
263
|
app get-all-envs : Get environment variables for an application
|
|
146
264
|
app set-env : Set environment variables for a service
|
|
147
265
|
device init : Initialize device
|
|
@@ -152,8 +270,8 @@ Subcommands:
|
|
|
152
270
|
|
|
153
271
|
To see the output logs, run the following command:
|
|
154
272
|
|
|
155
|
-
```
|
|
156
|
-
export ALWAYSAI_LOG_TO_CONSOLE=1
|
|
273
|
+
```bash
|
|
274
|
+
$ export ALWAYSAI_LOG_TO_CONSOLE=1
|
|
157
275
|
```
|
|
158
276
|
|
|
159
277
|
### Install the application on the device
|
|
@@ -161,7 +279,7 @@ export ALWAYSAI_LOG_TO_CONSOLE=1
|
|
|
161
279
|
Now you can install the application on the device using the device agent. Run
|
|
162
280
|
the following on the device where the Device Agent is installed:
|
|
163
281
|
|
|
164
|
-
```
|
|
282
|
+
```bash
|
|
165
283
|
$ aai-agent app install --project <project_id> --release <release_hash>
|
|
166
284
|
```
|
|
167
285
|
|
|
@@ -170,27 +288,27 @@ $ aai-agent app install --project <project_id> --release <release_hash>
|
|
|
170
288
|
Run the following commands on the device where the Device Agent is installed:
|
|
171
289
|
|
|
172
290
|
Get application status:
|
|
173
|
-
```
|
|
291
|
+
```bash
|
|
174
292
|
$ aai-agent app status --project <project_id>
|
|
175
293
|
```
|
|
176
294
|
|
|
177
295
|
Start the application:
|
|
178
|
-
```
|
|
296
|
+
```bash
|
|
179
297
|
$ aai-agent app start --project <project_id>
|
|
180
298
|
```
|
|
181
299
|
|
|
182
300
|
Show the application logs:
|
|
183
|
-
```
|
|
301
|
+
```bash
|
|
184
302
|
$ aai-agent app logs --project <project_id>
|
|
185
303
|
```
|
|
186
304
|
|
|
187
305
|
Stop the application:
|
|
188
|
-
```
|
|
306
|
+
```bash
|
|
189
307
|
$ aai-agent app stop --project <project_id>
|
|
190
308
|
```
|
|
191
309
|
|
|
192
310
|
Uninstall the application:
|
|
193
|
-
```
|
|
311
|
+
```bash
|
|
194
312
|
$ aai-agent app uninstall --project <project_id>
|
|
195
313
|
```
|
|
196
314
|
|
|
@@ -204,7 +322,7 @@ If a new version of a model is published for an existing model ID, and an
|
|
|
204
322
|
application is already configured to be using that model ID, updating to the
|
|
205
323
|
new model can simply be done with:
|
|
206
324
|
|
|
207
|
-
```
|
|
325
|
+
```bash
|
|
208
326
|
$ aai-agent app stop --project <project_id>
|
|
209
327
|
$ aai-agent app update-models --project <project_id>
|
|
210
328
|
$ aai-agent app start --project <project_id>
|
|
@@ -215,7 +333,7 @@ $ aai-agent app start --project <project_id>
|
|
|
215
333
|
If you'd like to install an entirely new model to an application, replacing the
|
|
216
334
|
model the app was originally configured with, run the following command:
|
|
217
335
|
|
|
218
|
-
```
|
|
336
|
+
```bash
|
|
219
337
|
$ aai-agent app stop --project <project_id>
|
|
220
338
|
$ aai-agent app replace-models --project <project_id> --models <model_id_1> [<model_id_2> ...]
|
|
221
339
|
$ aai-agent app start --project <project_id>
|
|
@@ -225,7 +343,7 @@ If you plan on using this method, you can make your application source model ID
|
|
|
225
343
|
agnostic by providing the model ID to edgeIQ in the following way:
|
|
226
344
|
|
|
227
345
|
Select the first model in the config list:
|
|
228
|
-
```
|
|
346
|
+
```python
|
|
229
347
|
obj_detect = edgeiq.ObjectDetection(edgeiq._globals.MODEL_ID_LIST[0])
|
|
230
348
|
```
|
|
231
349
|
|
|
@@ -233,13 +351,13 @@ obj_detect = edgeiq.ObjectDetection(edgeiq._globals.MODEL_ID_LIST[0])
|
|
|
233
351
|
|
|
234
352
|
To download a model package from the alwaysAI cloud and unpack to a specific directory, run:
|
|
235
353
|
|
|
236
|
-
```
|
|
354
|
+
```bash
|
|
237
355
|
$ aai-agent get-model-package <model ID> [--path <destination path>]
|
|
238
356
|
```
|
|
239
357
|
|
|
240
358
|
For example, to download `alwaysai/yolo_v3` to `~/alwaysai` run:
|
|
241
359
|
|
|
242
|
-
```
|
|
360
|
+
```bash
|
|
243
361
|
$ aai-agent get-model-package alwaysai/yolo_v3 --path ~/alwaysai
|
|
244
362
|
```
|
|
245
363
|
|
|
@@ -251,13 +369,13 @@ Once the command completes, you'll see the model package in the
|
|
|
251
369
|
The Device Agent enables you to set and update environment variables for all
|
|
252
370
|
services or a single service of you application.
|
|
253
371
|
|
|
254
|
-
```
|
|
372
|
+
```bash
|
|
255
373
|
$ aai-agent app set-env <key=val> --project <project_id> [--service <service>]
|
|
256
374
|
```
|
|
257
375
|
|
|
258
376
|
For example, to set the following environment variable for the `alwaysai`
|
|
259
377
|
service run the following command on the device:
|
|
260
378
|
|
|
261
|
-
```
|
|
379
|
+
```bash
|
|
262
380
|
$ aai-agent app set-env TEST_ENV=1 --project <project_id> --service alwaysai
|
|
263
381
|
```
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
2
2
|
import { parse, stringify } from 'yaml';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
buildApp,
|
|
5
|
+
getAppDir,
|
|
6
|
+
requireAppInstalled,
|
|
7
|
+
requireAppReady
|
|
8
|
+
} from './utils';
|
|
4
9
|
import { JsSpawner } from 'alwaysai/lib/util';
|
|
5
10
|
import compose from 'docker-compose';
|
|
6
11
|
import { assign, merge } from 'lodash';
|
|
7
12
|
import { AppJsonFile } from 'alwaysai/lib/core/app';
|
|
8
13
|
import { AppConfig } from '@alwaysai/app-configuration-schemas';
|
|
9
|
-
import { restartApp } from './status';
|
|
14
|
+
import { isAppStarted, restartApp } from './status';
|
|
10
15
|
import { logger } from '../util/logger';
|
|
11
16
|
|
|
12
17
|
export async function readAppCfgFile(props: {
|
|
13
18
|
projectId: string;
|
|
14
19
|
}): Promise<AppConfig> {
|
|
15
20
|
const { projectId } = props;
|
|
16
|
-
|
|
17
|
-
throw new Error(`App ${projectId} is not present!`);
|
|
18
|
-
}
|
|
21
|
+
await requireAppInstalled({ projectId });
|
|
19
22
|
const appDir = getAppDir(projectId);
|
|
20
23
|
const appJson = AppJsonFile(appDir);
|
|
21
24
|
if (!appJson.exists()) {
|
|
@@ -37,9 +40,6 @@ export async function writeAppCfgFile(props: {
|
|
|
37
40
|
appCfg: AppConfig;
|
|
38
41
|
}) {
|
|
39
42
|
const { projectId, appCfg } = props;
|
|
40
|
-
if (!(await AgentConfigFile().isAppPresent({ projectId }))) {
|
|
41
|
-
throw new Error(`App ${projectId} is not present!`);
|
|
42
|
-
}
|
|
43
43
|
const appDir = getAppDir(projectId);
|
|
44
44
|
const appJson = AppJsonFile(appDir);
|
|
45
45
|
try {
|
|
@@ -65,25 +65,22 @@ export async function updateAppCfgFile(props: {
|
|
|
65
65
|
|
|
66
66
|
export async function updateAppCfg(props: {
|
|
67
67
|
projectId: string;
|
|
68
|
-
appReleaseHash: string;
|
|
69
68
|
newAppCfg: AppConfig;
|
|
70
69
|
}) {
|
|
71
|
-
const { projectId,
|
|
72
|
-
logger.info(`Updating app config for ${projectId}
|
|
70
|
+
const { projectId, newAppCfg } = props;
|
|
71
|
+
logger.info(`Updating app config for ${projectId}`);
|
|
73
72
|
const appDir = getAppDir(projectId);
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
throw new Error('Application is not installed!');
|
|
86
|
-
}
|
|
74
|
+
await requireAppReady({ projectId });
|
|
75
|
+
|
|
76
|
+
const appReleaseHash = await AgentConfigFile().getAppVersion({
|
|
77
|
+
projectId
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await AgentConfigFile().setAppInstalling({
|
|
81
|
+
projectId,
|
|
82
|
+
version: appReleaseHash
|
|
83
|
+
});
|
|
87
84
|
|
|
88
85
|
await writeAppCfgFile({ projectId, appCfg: newAppCfg });
|
|
89
86
|
await buildApp({ appDir });
|
|
@@ -93,16 +90,21 @@ export async function updateAppCfg(props: {
|
|
|
93
90
|
version: appReleaseHash
|
|
94
91
|
});
|
|
95
92
|
|
|
96
|
-
await
|
|
93
|
+
if (await isAppStarted({ projectId })) {
|
|
94
|
+
await restartApp({ projectId });
|
|
95
|
+
}
|
|
97
96
|
|
|
98
|
-
logger.info(
|
|
97
|
+
logger.info(
|
|
98
|
+
`Updated app config and rebuilt ${projectId}: ${JSON.stringify(
|
|
99
|
+
newAppCfg,
|
|
100
|
+
null,
|
|
101
|
+
2
|
|
102
|
+
)}`
|
|
103
|
+
);
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
export async function readDockerCompose(props: { projectId: string }) {
|
|
102
107
|
const { projectId } = props;
|
|
103
|
-
if (!(await AgentConfigFile().isAppReady({ projectId }))) {
|
|
104
|
-
throw new Error(`App ${projectId} is not ready!`);
|
|
105
|
-
}
|
|
106
108
|
const appDir = getAppDir(projectId);
|
|
107
109
|
const spawner = JsSpawner({ path: appDir });
|
|
108
110
|
const composeContents = await spawner.readFile('docker-compose.yaml');
|
|
@@ -115,9 +117,6 @@ export async function writeDockerCompose(props: {
|
|
|
115
117
|
dockerCompose: any;
|
|
116
118
|
}) {
|
|
117
119
|
const { projectId, dockerCompose } = props;
|
|
118
|
-
if (!(await AgentConfigFile().isAppReady({ projectId }))) {
|
|
119
|
-
throw new Error(`App ${projectId} is not ready!`);
|
|
120
|
-
}
|
|
121
120
|
const appDir = getAppDir(projectId);
|
|
122
121
|
const spawner = JsSpawner({ path: appDir });
|
|
123
122
|
const composeOutput = stringify(dockerCompose);
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
2
|
+
import { readDockerCompose, writeDockerCompose } from './config';
|
|
3
|
+
import { getAllEnvs, setEnv } from './environment-variables';
|
|
4
|
+
import { isAppStarted, restartApp } from './status';
|
|
5
|
+
import { buildApp, requireAppReady } from './utils';
|
|
6
|
+
|
|
7
|
+
jest.mock('./config');
|
|
8
|
+
|
|
9
|
+
jest.mock('./utils');
|
|
10
|
+
jest.mocked(requireAppReady).mockResolvedValue();
|
|
11
|
+
jest.mocked(buildApp).mockResolvedValue();
|
|
12
|
+
|
|
13
|
+
jest.mock('./status');
|
|
14
|
+
jest.mocked(isAppStarted).mockResolvedValue(true);
|
|
15
|
+
jest.mocked(restartApp).mockResolvedValue();
|
|
16
|
+
|
|
17
|
+
jest.mock('../infrastructure/agent-config');
|
|
18
|
+
const mockSetAppInstalling = jest.fn().mockResolvedValue({});
|
|
19
|
+
const mockSetAppInstalled = jest.fn().mockResolvedValue({});
|
|
20
|
+
const mockGetAppVersion = jest.fn().mockResolvedValue('test-version');
|
|
21
|
+
jest.mocked(AgentConfigFile as jest.Mock).mockReturnValue({
|
|
22
|
+
setAppInstalling: mockSetAppInstalling,
|
|
23
|
+
setAppInstalled: mockSetAppInstalled,
|
|
24
|
+
getAppVersion: mockGetAppVersion
|
|
25
|
+
});
|
|
26
|
+
const projectId1 = 'test-project';
|
|
27
|
+
|
|
28
|
+
describe('Test environment variable get and set', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
jest.clearAllMocks();
|
|
31
|
+
});
|
|
32
|
+
describe('Test getAllEnvs()', () => {
|
|
33
|
+
test('get all envs with one service and no envs', async () => {
|
|
34
|
+
const compose = {
|
|
35
|
+
services: {
|
|
36
|
+
alwaysai: {}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
jest.mocked(readDockerCompose).mockResolvedValue(compose);
|
|
40
|
+
|
|
41
|
+
const envVars = await getAllEnvs({ projectId: projectId1 });
|
|
42
|
+
expect(jest.mocked(readDockerCompose)).toBeCalledWith({
|
|
43
|
+
projectId: projectId1
|
|
44
|
+
});
|
|
45
|
+
expect(envVars).toEqual({
|
|
46
|
+
alwaysai: {}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
// TODO: Add test with env file
|
|
50
|
+
test('get all envs with one service and two envs', async () => {
|
|
51
|
+
const compose = {
|
|
52
|
+
services: {
|
|
53
|
+
alwaysai: {
|
|
54
|
+
environment: ['TEST=1', 'TEST2=2']
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
jest.mocked(readDockerCompose).mockResolvedValue(compose);
|
|
59
|
+
|
|
60
|
+
const envVars = await getAllEnvs({ projectId: projectId1 });
|
|
61
|
+
expect(jest.mocked(readDockerCompose)).toBeCalledWith({
|
|
62
|
+
projectId: projectId1
|
|
63
|
+
});
|
|
64
|
+
expect(envVars).toEqual({
|
|
65
|
+
alwaysai: { TEST: '1', TEST2: '2' }
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
test('get all envs with two services and envs', async () => {
|
|
69
|
+
const compose = {
|
|
70
|
+
services: {
|
|
71
|
+
alwaysai: {
|
|
72
|
+
environment: ['TEST=3', 'TEST2=4']
|
|
73
|
+
},
|
|
74
|
+
edgeiq: {
|
|
75
|
+
environment: ['TEST3=5', 'TEST4=6']
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
jest.mocked(readDockerCompose).mockResolvedValue(compose);
|
|
80
|
+
|
|
81
|
+
const envVars = await getAllEnvs({ projectId: projectId1 });
|
|
82
|
+
expect(jest.mocked(readDockerCompose)).toBeCalledWith({
|
|
83
|
+
projectId: projectId1
|
|
84
|
+
});
|
|
85
|
+
expect(envVars).toEqual({
|
|
86
|
+
alwaysai: { TEST: '3', TEST2: '4' },
|
|
87
|
+
edgeiq: { TEST3: '5', TEST4: '6' }
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe('Test setEnv()', () => {
|
|
92
|
+
test('Set one env', async () => {
|
|
93
|
+
const compose = {
|
|
94
|
+
services: {
|
|
95
|
+
alwaysai: {}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
jest.mocked(readDockerCompose).mockResolvedValue(compose);
|
|
99
|
+
|
|
100
|
+
const envVars = { alwaysai: { TEST: '1' } };
|
|
101
|
+
await setEnv({ projectId: projectId1, envVars });
|
|
102
|
+
expect(jest.mocked(readDockerCompose)).toBeCalledWith({
|
|
103
|
+
projectId: projectId1
|
|
104
|
+
});
|
|
105
|
+
expect(jest.mocked(writeDockerCompose)).toBeCalledWith({
|
|
106
|
+
projectId: projectId1,
|
|
107
|
+
dockerCompose: {
|
|
108
|
+
services: {
|
|
109
|
+
alwaysai: {
|
|
110
|
+
environment: ['TEST=1']
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
test('Update one env', async () => {
|
|
117
|
+
const environment = ['TEST1=1', 'TEST2=2'];
|
|
118
|
+
const compose = {
|
|
119
|
+
services: {
|
|
120
|
+
alwaysai: { environment }
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
jest.mocked(readDockerCompose).mockResolvedValue(compose);
|
|
124
|
+
|
|
125
|
+
const envVars = { alwaysai: { TEST1: '2' } };
|
|
126
|
+
await setEnv({ projectId: projectId1, envVars });
|
|
127
|
+
expect(jest.mocked(readDockerCompose)).toBeCalledWith({
|
|
128
|
+
projectId: projectId1
|
|
129
|
+
});
|
|
130
|
+
expect(jest.mocked(writeDockerCompose)).toBeCalledWith({
|
|
131
|
+
projectId: projectId1,
|
|
132
|
+
dockerCompose: {
|
|
133
|
+
services: {
|
|
134
|
+
alwaysai: {
|
|
135
|
+
environment: ['TEST1=1', 'TEST2=2', 'TEST1=2']
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
test('Update one env with two services', async () => {
|
|
142
|
+
const environment = ['TEST1=3', 'TEST2=4'];
|
|
143
|
+
const compose = {
|
|
144
|
+
services: {
|
|
145
|
+
alwaysai: { environment },
|
|
146
|
+
other: { environment: ['OTHER_TEST=1'] }
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
jest.mocked(readDockerCompose).mockResolvedValue(compose);
|
|
150
|
+
|
|
151
|
+
const envVars = { alwaysai: { TEST1: '2' } };
|
|
152
|
+
await setEnv({ projectId: projectId1, envVars });
|
|
153
|
+
expect(jest.mocked(readDockerCompose)).toBeCalledWith({
|
|
154
|
+
projectId: projectId1
|
|
155
|
+
});
|
|
156
|
+
expect(jest.mocked(writeDockerCompose)).toBeCalledWith({
|
|
157
|
+
projectId: projectId1,
|
|
158
|
+
dockerCompose: {
|
|
159
|
+
services: {
|
|
160
|
+
alwaysai: {
|
|
161
|
+
environment: ['TEST1=3', 'TEST2=4', 'TEST1=2']
|
|
162
|
+
},
|
|
163
|
+
other: {
|
|
164
|
+
environment: ['OTHER_TEST=1']
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|