@alwaysai/device-agent 2.0.2-0 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/application-control/config.js +1 -1
- package/lib/application-control/config.js.map +1 -1
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +7 -3
- package/lib/application-control/install.js.map +1 -1
- package/lib/cloud-connection/base-message-handler.d.ts.map +1 -1
- package/lib/cloud-connection/base-message-handler.js +5 -4
- package/lib/cloud-connection/base-message-handler.js.map +1 -1
- package/lib/cloud-connection/connection-manager.d.ts +3 -4
- package/lib/cloud-connection/connection-manager.d.ts.map +1 -1
- package/lib/cloud-connection/connection-manager.js +22 -40
- package/lib/cloud-connection/connection-manager.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +3 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +52 -46
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/device-agent-message-handler.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-message-handler.js +19 -18
- package/lib/cloud-connection/device-agent-message-handler.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +11 -4
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/passthrough-handler.d.ts +3 -3
- package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
- package/lib/cloud-connection/passthrough-handler.js +97 -74
- package/lib/cloud-connection/passthrough-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +3 -3
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow.d.ts.map +1 -1
- package/lib/cloud-connection/shadow.js +1 -1
- package/lib/cloud-connection/shadow.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
- package/lib/cloud-connection/transaction-manager.js +17 -7
- package/lib/cloud-connection/transaction-manager.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.test.js +52 -44
- package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
- package/lib/device-control/device-control.d.ts.map +1 -1
- package/lib/device-control/device-control.js +13 -9
- package/lib/device-control/device-control.js.map +1 -1
- package/lib/docker/docker-compose.d.ts.map +1 -1
- package/lib/docker/docker-compose.js +1 -1
- package/lib/docker/docker-compose.js.map +1 -1
- package/lib/environment.d.ts +3 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js +12 -1
- package/lib/environment.js.map +1 -1
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -1
- package/lib/infrastructure/config-check-utility.js +2 -2
- package/lib/infrastructure/config-check-utility.js.map +1 -1
- package/lib/infrastructure/legacy-migration/legacy-migration.d.ts.map +1 -1
- package/lib/infrastructure/legacy-migration/legacy-migration.js +6 -10
- package/lib/infrastructure/legacy-migration/legacy-migration.js.map +1 -1
- package/lib/jobs/job-handler.d.ts +1 -1
- package/lib/jobs/job-handler.d.ts.map +1 -1
- package/lib/jobs/job-handler.js +4 -4
- package/lib/jobs/job-handler.js.map +1 -1
- package/lib/local-connection/rabbitmq-container.d.ts +6 -0
- package/lib/local-connection/rabbitmq-container.d.ts.map +1 -0
- package/lib/local-connection/rabbitmq-container.js +111 -0
- package/lib/local-connection/rabbitmq-container.js.map +1 -0
- package/lib/local-connection/rabbitmq-container.test.d.ts +2 -0
- package/lib/local-connection/rabbitmq-container.test.d.ts.map +1 -0
- package/lib/local-connection/rabbitmq-container.test.js +219 -0
- package/lib/local-connection/rabbitmq-container.test.js.map +1 -0
- package/lib/subcommands/app/analytics.d.ts.map +1 -1
- package/lib/subcommands/app/analytics.js +2 -3
- package/lib/subcommands/app/analytics.js.map +1 -1
- package/lib/subcommands/app/env-vars.d.ts.map +1 -1
- package/lib/subcommands/app/env-vars.js +4 -6
- package/lib/subcommands/app/env-vars.js.map +1 -1
- package/lib/subcommands/app/models.d.ts.map +1 -1
- package/lib/subcommands/app/models.js +2 -3
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/app/shadow.d.ts.map +1 -1
- package/lib/subcommands/app/shadow.js +4 -6
- package/lib/subcommands/app/shadow.js.map +1 -1
- package/lib/subcommands/app/version.d.ts.map +1 -1
- package/lib/subcommands/app/version.js +6 -9
- package/lib/subcommands/app/version.js.map +1 -1
- package/lib/subcommands/device/clean.d.ts.map +1 -1
- package/lib/subcommands/device/clean.js +15 -17
- package/lib/subcommands/device/clean.js.map +1 -1
- package/lib/subcommands/device/index.d.ts.map +1 -1
- package/lib/subcommands/device/index.js +3 -1
- package/lib/subcommands/device/index.js.map +1 -1
- package/lib/subcommands/device/local-connection.d.ts +2 -0
- package/lib/subcommands/device/local-connection.d.ts.map +1 -0
- package/lib/subcommands/device/local-connection.js +17 -0
- package/lib/subcommands/device/local-connection.js.map +1 -0
- package/lib/subcommands/index.d.ts +4 -1
- package/lib/subcommands/index.d.ts.map +1 -1
- package/lib/subcommands/index.js +1 -3
- package/lib/subcommands/index.js.map +1 -1
- package/lib/util/check-for-updates.d.ts.map +1 -1
- package/lib/util/check-for-updates.js +2 -2
- package/lib/util/check-for-updates.js.map +1 -1
- package/lib/util/file.d.ts.map +1 -1
- package/lib/util/file.js +6 -1
- package/lib/util/file.js.map +1 -1
- package/lib/util/file.test.js +1 -1
- package/lib/util/file.test.js.map +1 -1
- package/lib/util/get-device-id.d.ts.map +1 -1
- package/lib/util/get-device-id.js +1 -1
- package/lib/util/get-device-id.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +16 -4
- package/src/application-control/config.ts +1 -1
- package/src/application-control/install.ts +11 -5
- package/src/cloud-connection/base-message-handler.ts +10 -5
- package/src/cloud-connection/connection-manager.ts +23 -45
- package/src/cloud-connection/device-agent-cloud-connection.ts +97 -89
- package/src/cloud-connection/device-agent-message-handler.ts +10 -7
- package/src/cloud-connection/live-updates-handler.ts +12 -5
- package/src/cloud-connection/passthrough-handler.ts +79 -38
- package/src/cloud-connection/shadow-handler.ts +3 -3
- package/src/cloud-connection/shadow.ts +3 -1
- package/src/cloud-connection/transaction-manager.test.ts +60 -41
- package/src/cloud-connection/transaction-manager.ts +26 -12
- package/src/device-control/device-control.ts +23 -13
- package/src/docker/docker-compose.ts +3 -1
- package/src/environment.ts +17 -0
- package/src/index.ts +19 -0
- package/src/infrastructure/config-check-utility.ts +2 -2
- package/src/infrastructure/legacy-migration/legacy-migration.ts +8 -13
- package/src/jobs/job-handler.ts +4 -4
- package/src/local-connection/rabbitmq-container.test.ts +255 -0
- package/src/local-connection/rabbitmq-container.ts +151 -0
- package/src/subcommands/app/analytics.ts +2 -3
- package/src/subcommands/app/env-vars.ts +4 -6
- package/src/subcommands/app/models.ts +2 -3
- package/src/subcommands/app/shadow.ts +4 -6
- package/src/subcommands/app/version.ts +7 -8
- package/src/subcommands/device/clean.ts +20 -19
- package/src/subcommands/device/index.ts +3 -1
- package/src/subcommands/device/local-connection.ts +16 -0
- package/src/subcommands/index.ts +1 -3
- package/src/util/check-for-updates.ts +4 -2
- package/src/util/file.test.ts +1 -1
- package/src/util/file.ts +7 -1
- package/src/util/get-device-id.ts +3 -1
- package/lib/local-connection/rabbitmq-connection.d.ts +0 -7
- package/lib/local-connection/rabbitmq-connection.d.ts.map +0 -1
- package/lib/local-connection/rabbitmq-connection.js +0 -95
- package/lib/local-connection/rabbitmq-connection.js.map +0 -1
- package/lib/subcommands/rabbitmq-connection.d.ts +0 -2
- package/lib/subcommands/rabbitmq-connection.d.ts.map +0 -1
- package/lib/subcommands/rabbitmq-connection.js +0 -14
- package/lib/subcommands/rabbitmq-connection.js.map +0 -1
- package/src/local-connection/rabbitmq-connection.ts +0 -124
- package/src/subcommands/rabbitmq-connection.ts +0 -11
package/src/jobs/job-handler.ts
CHANGED
|
@@ -36,7 +36,7 @@ export class JobHandler extends BaseHandler implements MessageHandler {
|
|
|
36
36
|
private jobId = '';
|
|
37
37
|
|
|
38
38
|
public async handle(message: any, topic: string): Promise<void> {
|
|
39
|
-
const TOPICS = this.
|
|
39
|
+
const TOPICS = this.getJobTopics();
|
|
40
40
|
|
|
41
41
|
try {
|
|
42
42
|
switch (topic) {
|
|
@@ -54,7 +54,7 @@ export class JobHandler extends BaseHandler implements MessageHandler {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
getJobTopics() {
|
|
58
58
|
const TOPICS = {
|
|
59
59
|
NOTIFY_NEXT: `$aws/things/${this.clientId}/jobs/notify-next`,
|
|
60
60
|
START_NEXT: `$aws/things/${this.clientId}/jobs/start-next`,
|
|
@@ -67,7 +67,7 @@ export class JobHandler extends BaseHandler implements MessageHandler {
|
|
|
67
67
|
private async notifyNext(message: any): Promise<void> {
|
|
68
68
|
// Triggers when a job is completed and we need to notify the next job
|
|
69
69
|
logger.info('Invoking NOTIFY_NEXT handler');
|
|
70
|
-
const TOPICS = this.
|
|
70
|
+
const TOPICS = this.getJobTopics();
|
|
71
71
|
this.publisher.publish(TOPICS.START_NEXT, JSON.stringify({}));
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -121,7 +121,7 @@ export class JobHandler extends BaseHandler implements MessageHandler {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
private updateJobStatus(status: string, message?: string): void {
|
|
124
|
-
const { UPDATE } = this.
|
|
124
|
+
const { UPDATE } = this.getJobTopics();
|
|
125
125
|
const payload = {
|
|
126
126
|
status,
|
|
127
127
|
statusDetails: {
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkRabbitMQContainerRunning,
|
|
3
|
+
runRabbitMQContainer,
|
|
4
|
+
stopRabbitMQContainer
|
|
5
|
+
} from './rabbitmq-container';
|
|
6
|
+
|
|
7
|
+
const mockJsSpawner = {
|
|
8
|
+
exists: jest.fn(),
|
|
9
|
+
mkdirp: jest.fn(),
|
|
10
|
+
writeFile: jest.fn()
|
|
11
|
+
};
|
|
12
|
+
jest.mock('alwaysai/lib/util/spawner', () => {
|
|
13
|
+
return {
|
|
14
|
+
JsSpawner: jest.fn(() => mockJsSpawner)
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
jest.mock('../docker/docker-compose', () => {
|
|
19
|
+
return {
|
|
20
|
+
compose: {
|
|
21
|
+
ps: jest.fn(),
|
|
22
|
+
upAll: jest.fn(),
|
|
23
|
+
down: jest.fn()
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
import { compose } from '../docker/docker-compose';
|
|
28
|
+
|
|
29
|
+
describe('Test check RabbitMQ container running', () => {
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
jest.clearAllMocks();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('Compose file not found', async () => {
|
|
35
|
+
mockJsSpawner.exists.mockResolvedValue(false);
|
|
36
|
+
|
|
37
|
+
const running = await checkRabbitMQContainerRunning();
|
|
38
|
+
|
|
39
|
+
expect(running).toBeFalsy();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('Container not found', async () => {
|
|
43
|
+
mockJsSpawner.exists.mockResolvedValue(true);
|
|
44
|
+
jest.mocked(compose.ps).mockResolvedValue({
|
|
45
|
+
exitCode: 0,
|
|
46
|
+
out: '',
|
|
47
|
+
err: '',
|
|
48
|
+
data: {
|
|
49
|
+
services: []
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const running = await checkRabbitMQContainerRunning();
|
|
54
|
+
|
|
55
|
+
expect(running).toBeFalsy();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('Container is not up', async () => {
|
|
59
|
+
mockJsSpawner.exists.mockResolvedValue(true);
|
|
60
|
+
jest.mocked(compose.ps).mockResolvedValue({
|
|
61
|
+
exitCode: 0,
|
|
62
|
+
out: '',
|
|
63
|
+
err: '',
|
|
64
|
+
data: {
|
|
65
|
+
services: [
|
|
66
|
+
{
|
|
67
|
+
name: 'alwaysAIRabbitMQContainer',
|
|
68
|
+
command: '',
|
|
69
|
+
state: 'Down',
|
|
70
|
+
ports: []
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const running = await checkRabbitMQContainerRunning();
|
|
77
|
+
|
|
78
|
+
expect(running).toBeFalsy();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('Container is up', async () => {
|
|
82
|
+
mockJsSpawner.exists.mockResolvedValue(true);
|
|
83
|
+
jest.mocked(compose.ps).mockResolvedValue({
|
|
84
|
+
exitCode: 0,
|
|
85
|
+
out: '',
|
|
86
|
+
err: '',
|
|
87
|
+
data: {
|
|
88
|
+
services: [
|
|
89
|
+
{
|
|
90
|
+
name: 'alwaysAIRabbitMQContainer',
|
|
91
|
+
command: '',
|
|
92
|
+
state: 'Up',
|
|
93
|
+
ports: []
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const running = await checkRabbitMQContainerRunning();
|
|
100
|
+
|
|
101
|
+
expect(running).toBeTruthy();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('Test run RabbitMQ container', () => {
|
|
106
|
+
afterEach(() => {
|
|
107
|
+
jest.clearAllMocks();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('Compose file write fails', async () => {
|
|
111
|
+
mockJsSpawner.exists.mockResolvedValueOnce(false);
|
|
112
|
+
mockJsSpawner.mkdirp.mockImplementation(async () => {
|
|
113
|
+
return;
|
|
114
|
+
});
|
|
115
|
+
mockJsSpawner.writeFile.mockImplementation(async () => {
|
|
116
|
+
throw new Error('Failed to write file!');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const running = await runRabbitMQContainer();
|
|
120
|
+
|
|
121
|
+
expect(running).toBeFalsy();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('Compose up fails', async () => {
|
|
125
|
+
mockJsSpawner.exists.mockResolvedValueOnce(true);
|
|
126
|
+
mockJsSpawner.writeFile.mockImplementation(async () => {
|
|
127
|
+
return;
|
|
128
|
+
});
|
|
129
|
+
jest.mocked(compose.upAll).mockResolvedValue({
|
|
130
|
+
exitCode: 1,
|
|
131
|
+
out: '',
|
|
132
|
+
err: ''
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const running = await runRabbitMQContainer();
|
|
136
|
+
|
|
137
|
+
expect(running).toBeFalsy();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('Container fails to come up', async () => {
|
|
141
|
+
// exists(deviceAgentConfigPath)
|
|
142
|
+
mockJsSpawner.exists.mockResolvedValueOnce(true);
|
|
143
|
+
mockJsSpawner.writeFile.mockImplementation(async () => {
|
|
144
|
+
return;
|
|
145
|
+
});
|
|
146
|
+
jest.mocked(compose.upAll).mockResolvedValue({
|
|
147
|
+
exitCode: 0,
|
|
148
|
+
out: '',
|
|
149
|
+
err: ''
|
|
150
|
+
});
|
|
151
|
+
// exists(deviceAgentDockerComposePath)
|
|
152
|
+
mockJsSpawner.exists.mockResolvedValueOnce(true);
|
|
153
|
+
jest.mocked(compose.ps).mockResolvedValue({
|
|
154
|
+
exitCode: 0,
|
|
155
|
+
out: '',
|
|
156
|
+
err: '',
|
|
157
|
+
data: {
|
|
158
|
+
services: [
|
|
159
|
+
{
|
|
160
|
+
name: 'alwaysAIRabbitMQContainer',
|
|
161
|
+
command: '',
|
|
162
|
+
state: 'Down',
|
|
163
|
+
ports: []
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const running = await runRabbitMQContainer({ max_attempts: 2 });
|
|
170
|
+
|
|
171
|
+
expect(jest.mocked(compose.ps)).toBeCalledTimes(2);
|
|
172
|
+
expect(running).toBeFalsy();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('Container comes up successfully', async () => {
|
|
176
|
+
// exists(deviceAgentConfigPath)
|
|
177
|
+
mockJsSpawner.exists.mockResolvedValueOnce(true);
|
|
178
|
+
mockJsSpawner.writeFile.mockImplementation(async () => {
|
|
179
|
+
return;
|
|
180
|
+
});
|
|
181
|
+
jest.mocked(compose.upAll).mockResolvedValue({
|
|
182
|
+
exitCode: 0,
|
|
183
|
+
out: '',
|
|
184
|
+
err: ''
|
|
185
|
+
});
|
|
186
|
+
// exists(deviceAgentDockerComposePath)
|
|
187
|
+
mockJsSpawner.exists.mockResolvedValueOnce(true);
|
|
188
|
+
// Check status twice, one with container still down and next up
|
|
189
|
+
jest.mocked(compose.ps).mockResolvedValueOnce({
|
|
190
|
+
exitCode: 0,
|
|
191
|
+
out: '',
|
|
192
|
+
err: '',
|
|
193
|
+
data: {
|
|
194
|
+
services: [
|
|
195
|
+
{
|
|
196
|
+
name: 'alwaysAIRabbitMQContainer',
|
|
197
|
+
command: '',
|
|
198
|
+
state: 'Down',
|
|
199
|
+
ports: []
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
jest.mocked(compose.ps).mockResolvedValueOnce({
|
|
205
|
+
exitCode: 0,
|
|
206
|
+
out: '',
|
|
207
|
+
err: '',
|
|
208
|
+
data: {
|
|
209
|
+
services: [
|
|
210
|
+
{
|
|
211
|
+
name: 'alwaysAIRabbitMQContainer',
|
|
212
|
+
command: '',
|
|
213
|
+
state: 'Up',
|
|
214
|
+
ports: []
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const running = await runRabbitMQContainer({ max_attempts: 2 });
|
|
221
|
+
|
|
222
|
+
expect(jest.mocked(compose.ps)).toBeCalledTimes(2);
|
|
223
|
+
expect(running).toBeTruthy();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('Test stopping RabbitMQ container', () => {
|
|
228
|
+
afterEach(() => {
|
|
229
|
+
jest.clearAllMocks();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('Compose down fails', async () => {
|
|
233
|
+
jest.mocked(compose.down).mockResolvedValue({
|
|
234
|
+
exitCode: 1,
|
|
235
|
+
out: '',
|
|
236
|
+
err: ''
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const stopped = await stopRabbitMQContainer();
|
|
240
|
+
|
|
241
|
+
expect(stopped).toBeFalsy();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('Stop succeeds', async () => {
|
|
245
|
+
jest.mocked(compose.down).mockResolvedValue({
|
|
246
|
+
exitCode: 0,
|
|
247
|
+
out: '',
|
|
248
|
+
err: ''
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const stopped = await stopRabbitMQContainer();
|
|
252
|
+
|
|
253
|
+
expect(stopped).toBeTruthy();
|
|
254
|
+
});
|
|
255
|
+
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { JsSpawner, stringifyError } from 'alwaysai/lib/util';
|
|
2
|
+
import * as YAML from 'yaml';
|
|
3
|
+
import { compose } from '../docker/docker-compose';
|
|
4
|
+
import {
|
|
5
|
+
getDeviceAgentConfigPath,
|
|
6
|
+
getDeviceAgentDockerComposePath
|
|
7
|
+
} from '../util/directories';
|
|
8
|
+
import { logger } from '../util/logger';
|
|
9
|
+
import sleep from '../util/sleep';
|
|
10
|
+
import { LOCAL_CONNECTION_PORT } from './constants';
|
|
11
|
+
|
|
12
|
+
const rabbitMQServiceName = 'alwaysAIRabbitMQ';
|
|
13
|
+
const rabbitMQContainerName = 'alwaysAIRabbitMQContainer';
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
Utility functions to control the Local Connection container (RabbitMQ).
|
|
17
|
+
|
|
18
|
+
All exported functions handle their own errors and log their status, returning a
|
|
19
|
+
boolean indicating whether the action was successful.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export async function checkRabbitMQContainerRunning(): Promise<boolean> {
|
|
23
|
+
logger.debug('Checking Local Connection Container status');
|
|
24
|
+
const spawner = JsSpawner();
|
|
25
|
+
if (!(await spawner.exists(getDeviceAgentDockerComposePath()))) {
|
|
26
|
+
logger.warn('Local Connection configuration file is not present');
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const containerData = await compose.ps({ cwd: getDeviceAgentConfigPath() });
|
|
30
|
+
for (const service of containerData.data.services) {
|
|
31
|
+
if (service?.name === rabbitMQContainerName) {
|
|
32
|
+
if (service.state.includes('Up')) {
|
|
33
|
+
logger.debug('Local Connection container is up.');
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
logger.debug(
|
|
37
|
+
`Local Connection container is not up. Status: ${service.state}`
|
|
38
|
+
);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
logger.warn(
|
|
43
|
+
`Local Connection container not found in container data: ${JSON.stringify(
|
|
44
|
+
containerData,
|
|
45
|
+
null,
|
|
46
|
+
2
|
|
47
|
+
)}`
|
|
48
|
+
);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function writeRabbitMQDockerComposeFile() {
|
|
53
|
+
const spawner = JsSpawner();
|
|
54
|
+
if (!(await spawner.exists(getDeviceAgentConfigPath()))) {
|
|
55
|
+
await JsSpawner().mkdirp(getDeviceAgentConfigPath());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
logger.debug(
|
|
59
|
+
`Writing Local Connection Docker compose files with Local Connection port: ${LOCAL_CONNECTION_PORT}`
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const rabbitmqDockerComposeCmd = {
|
|
63
|
+
services: {
|
|
64
|
+
[rabbitMQServiceName]: {
|
|
65
|
+
container_name: rabbitMQContainerName,
|
|
66
|
+
image: 'rabbitmq:3.11',
|
|
67
|
+
ports: [`${LOCAL_CONNECTION_PORT}:${LOCAL_CONNECTION_PORT}`],
|
|
68
|
+
hostname: 'my-rabbit',
|
|
69
|
+
restart: 'on-failure',
|
|
70
|
+
environment: [`RABBITMQ_NODE_PORT=${LOCAL_CONNECTION_PORT}`],
|
|
71
|
+
volumes: [
|
|
72
|
+
'./rabbitmq/:/var/lib/rabbitmq/',
|
|
73
|
+
'./rabbitmq/log/:/var/log/rabbitmq/'
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const RabbitMQDockerComposeYaml = YAML.stringify(rabbitmqDockerComposeCmd);
|
|
80
|
+
await spawner.writeFile(
|
|
81
|
+
getDeviceAgentDockerComposePath(),
|
|
82
|
+
RabbitMQDockerComposeYaml
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function runRabbitMQContainer(opts?: {
|
|
87
|
+
max_attempts: number;
|
|
88
|
+
}): Promise<boolean> {
|
|
89
|
+
const max_attempts = opts?.max_attempts || 10;
|
|
90
|
+
logger.debug('Starting the Local Connection container');
|
|
91
|
+
try {
|
|
92
|
+
await writeRabbitMQDockerComposeFile();
|
|
93
|
+
const upOut = await compose.upAll({
|
|
94
|
+
cwd: getDeviceAgentConfigPath()
|
|
95
|
+
});
|
|
96
|
+
logger.debug(
|
|
97
|
+
`Docker compose up for Local Connection:\n${JSON.stringify(
|
|
98
|
+
upOut,
|
|
99
|
+
null,
|
|
100
|
+
2
|
|
101
|
+
)}`
|
|
102
|
+
);
|
|
103
|
+
if (upOut.exitCode !== 0) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Failed to start Local Connection! stdout=${upOut.out} stderr=${upOut.err}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
// check if the container is up
|
|
109
|
+
let attempts = 0;
|
|
110
|
+
while (!(await checkRabbitMQContainerRunning())) {
|
|
111
|
+
attempts++;
|
|
112
|
+
if (attempts >= max_attempts) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
'Local Connection container failed to come up after start!'
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
await sleep(1000);
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
} catch (e) {
|
|
121
|
+
logger.error(
|
|
122
|
+
`Unable to start Local Connection container!\n${stringifyError(e)}`
|
|
123
|
+
);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export async function stopRabbitMQContainer(): Promise<boolean> {
|
|
129
|
+
logger.debug(`Stopping alwaysAI Local Connection container`);
|
|
130
|
+
try {
|
|
131
|
+
const downOut = await compose.down({ cwd: getDeviceAgentConfigPath() });
|
|
132
|
+
logger.debug(
|
|
133
|
+
`Local Connection docker-compose down: ${JSON.stringify(
|
|
134
|
+
downOut,
|
|
135
|
+
null,
|
|
136
|
+
2
|
|
137
|
+
)}`
|
|
138
|
+
);
|
|
139
|
+
if (downOut.exitCode !== 0) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Failed to stop Local Connection! stdout=${downOut.out} stderr=${downOut.err}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
} catch (e) {
|
|
146
|
+
logger.error(
|
|
147
|
+
`Unable to stop Local Connection Container:\n${stringifyError(e)}`
|
|
148
|
+
);
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -80,6 +80,7 @@ export const setAnalyticsCfgCliLeaf = CliLeaf({
|
|
|
80
80
|
appConfig: JSON.stringify(appCfg) // Pack app config as string as dictated by schema
|
|
81
81
|
}
|
|
82
82
|
};
|
|
83
|
+
await deviceAgent.waitForConnection();
|
|
83
84
|
deviceAgent.publisher.publish(
|
|
84
85
|
getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
|
|
85
86
|
JSON.stringify(
|
|
@@ -92,9 +93,7 @@ export const setAnalyticsCfgCliLeaf = CliLeaf({
|
|
|
92
93
|
// Sleep for extra time to ensure time for shadow response
|
|
93
94
|
await sleep(10000);
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
await sleep(1000);
|
|
97
|
-
}
|
|
96
|
+
await deviceAgent.waitForCmd(project);
|
|
98
97
|
await deviceAgent.stop();
|
|
99
98
|
}
|
|
100
99
|
});
|
|
@@ -66,6 +66,7 @@ export const setEnvCliLeaf = CliLeaf({
|
|
|
66
66
|
envVars: JSON.stringify(envVars)
|
|
67
67
|
}
|
|
68
68
|
};
|
|
69
|
+
await deviceAgent.waitForConnection();
|
|
69
70
|
deviceAgent.publisher.publish(
|
|
70
71
|
getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
|
|
71
72
|
JSON.stringify(
|
|
@@ -78,9 +79,7 @@ export const setEnvCliLeaf = CliLeaf({
|
|
|
78
79
|
// Sleep for extra time to ensure time for shadow response
|
|
79
80
|
await sleep(10000);
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
await sleep(1000);
|
|
83
|
-
}
|
|
82
|
+
await deviceAgent.waitForCmd(projectId);
|
|
84
83
|
await deviceAgent.stop();
|
|
85
84
|
}
|
|
86
85
|
});
|
|
@@ -128,6 +127,7 @@ export const rmEnvCliLeaf = CliLeaf({
|
|
|
128
127
|
envVars: JSON.stringify(newEnvs)
|
|
129
128
|
}
|
|
130
129
|
};
|
|
130
|
+
await deviceAgent.waitForConnection();
|
|
131
131
|
deviceAgent.publisher.publish(
|
|
132
132
|
getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
|
|
133
133
|
JSON.stringify(
|
|
@@ -140,9 +140,7 @@ export const rmEnvCliLeaf = CliLeaf({
|
|
|
140
140
|
// Sleep for extra time to ensure time for shadow response
|
|
141
141
|
await sleep(10000);
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
await sleep(1000);
|
|
145
|
-
}
|
|
143
|
+
await deviceAgent.waitForCmd(projectId);
|
|
146
144
|
await deviceAgent.stop();
|
|
147
145
|
}
|
|
148
146
|
});
|
|
@@ -54,6 +54,7 @@ export const addModelCliLeaf = CliLeaf({
|
|
|
54
54
|
appConfig: JSON.stringify(newAppCfg)
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
|
+
await deviceAgent.waitForConnection();
|
|
57
58
|
deviceAgent.publisher.publish(
|
|
58
59
|
getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
|
|
59
60
|
JSON.stringify(
|
|
@@ -66,9 +67,7 @@ export const addModelCliLeaf = CliLeaf({
|
|
|
66
67
|
// Sleep for extra time to ensure time for shadow response
|
|
67
68
|
await sleep(10000);
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
await sleep(1000);
|
|
71
|
-
}
|
|
70
|
+
await deviceAgent.waitForCmd(projectId);
|
|
72
71
|
await deviceAgent.stop();
|
|
73
72
|
}
|
|
74
73
|
});
|
|
@@ -14,13 +14,12 @@ export const getShadowCliLeaf = CliLeaf({
|
|
|
14
14
|
async action(_, opts) {
|
|
15
15
|
const { project } = opts;
|
|
16
16
|
const deviceAgent = new DeviceAgentCloudConnection();
|
|
17
|
+
await deviceAgent.waitForConnection();
|
|
17
18
|
deviceAgent.shadowHandler.getProjectShadowUpdates();
|
|
18
19
|
|
|
19
20
|
// 5 seconds should cover the response wait time
|
|
20
21
|
await sleep(5000);
|
|
21
|
-
|
|
22
|
-
await sleep(1000);
|
|
23
|
-
}
|
|
22
|
+
await deviceAgent.waitForCmd(project);
|
|
24
23
|
await deviceAgent.stop();
|
|
25
24
|
}
|
|
26
25
|
});
|
|
@@ -37,13 +36,12 @@ export const updateShadowCliLeaf = CliLeaf({
|
|
|
37
36
|
async action(_, opts) {
|
|
38
37
|
const { project } = opts;
|
|
39
38
|
const deviceAgent = new DeviceAgentCloudConnection();
|
|
39
|
+
await deviceAgent.waitForConnection();
|
|
40
40
|
await deviceAgent.shadowHandler.updateProjectShadow(project);
|
|
41
41
|
|
|
42
42
|
// 5 seconds should cover the response wait time
|
|
43
43
|
await sleep(5000);
|
|
44
|
-
|
|
45
|
-
await sleep(1000);
|
|
46
|
-
}
|
|
44
|
+
await deviceAgent.waitForCmd(project);
|
|
47
45
|
await deviceAgent.stop();
|
|
48
46
|
}
|
|
49
47
|
});
|
|
@@ -10,6 +10,7 @@ import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-
|
|
|
10
10
|
import { AgentConfigFile } from '../../infrastructure/agent-config';
|
|
11
11
|
import sleep from '../../util/sleep';
|
|
12
12
|
import { logger } from '../../util/logger';
|
|
13
|
+
import { device } from 'alwaysai/lib/components';
|
|
13
14
|
|
|
14
15
|
export const listAppsCliLeaf = CliLeaf({
|
|
15
16
|
name: 'list',
|
|
@@ -81,10 +82,9 @@ export const installAppCliLeaf = CliLeaf({
|
|
|
81
82
|
envVars
|
|
82
83
|
}
|
|
83
84
|
};
|
|
84
|
-
await deviceAgent.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
85
|
+
await deviceAgent.waitForConnection();
|
|
86
|
+
await deviceAgent.sendCliCmd(topic, message);
|
|
87
|
+
await deviceAgent.waitForCmd(project);
|
|
88
88
|
await deviceAgent.stop();
|
|
89
89
|
}
|
|
90
90
|
});
|
|
@@ -112,10 +112,9 @@ export const uninstallAppCliLeaf = CliLeaf({
|
|
|
112
112
|
projectId: project
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
|
-
await deviceAgent.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
115
|
+
await deviceAgent.waitForConnection();
|
|
116
|
+
await deviceAgent.sendCliCmd(topic, message);
|
|
117
|
+
await deviceAgent.waitForCmd(project);
|
|
119
118
|
await deviceAgent.stop();
|
|
120
119
|
}
|
|
121
120
|
});
|
|
@@ -11,7 +11,7 @@ import { AgentConfigFile } from '../../infrastructure/agent-config';
|
|
|
11
11
|
import {
|
|
12
12
|
checkRabbitMQContainerRunning,
|
|
13
13
|
stopRabbitMQContainer
|
|
14
|
-
} from '../../local-connection/rabbitmq-
|
|
14
|
+
} from '../../local-connection/rabbitmq-container';
|
|
15
15
|
import {
|
|
16
16
|
APP_ROOT,
|
|
17
17
|
CREDENTIALS_FILE_PATH,
|
|
@@ -20,28 +20,24 @@ import {
|
|
|
20
20
|
} from '../../util/directories';
|
|
21
21
|
import { safeRimraf } from '../../util/file';
|
|
22
22
|
import { logger } from '../../util/logger';
|
|
23
|
+
import { existsSync } from 'fs';
|
|
23
24
|
|
|
24
25
|
export const cleanCliLeaf = CliLeaf({
|
|
25
26
|
name: 'clean',
|
|
26
27
|
description: 'Remove all provisioning files',
|
|
27
28
|
async action(_, opts) {
|
|
28
29
|
logger.info('Removing provisioning files.');
|
|
29
|
-
|
|
30
|
-
if (await
|
|
31
|
-
|
|
30
|
+
if (await checkRabbitMQContainerRunning()) {
|
|
31
|
+
if ((await stopRabbitMQContainer()) === false) {
|
|
32
|
+
logger.error(
|
|
33
|
+
`You may need to manually stop the container by running docker-compose down in the following directory: ${getDeviceAgentDockerComposePath()}`
|
|
34
|
+
);
|
|
32
35
|
}
|
|
33
|
-
} catch (e) {
|
|
34
|
-
logger.error(
|
|
35
|
-
`You may need to manually stop the container by running docker-compose down in the following directory: ${getDeviceAgentDockerComposePath()}`
|
|
36
|
-
);
|
|
37
|
-
logger.debug(
|
|
38
|
-
`Error in checking / stopping RabbitMQ container!\n${stringifyError(e)}`
|
|
39
|
-
);
|
|
40
36
|
}
|
|
41
37
|
|
|
42
38
|
await safeRimraf(getDeviceAgentConfigPath());
|
|
43
39
|
|
|
44
|
-
logger.debug('Checking for alwaysAI applications still
|
|
40
|
+
logger.debug('Checking for alwaysAI applications still installed');
|
|
45
41
|
const apps = await AgentConfigFile().getApps();
|
|
46
42
|
const uninstallAppPromises: Promise<void>[] = [];
|
|
47
43
|
for (const app of apps) {
|
|
@@ -60,13 +56,18 @@ export const cleanCliLeaf = CliLeaf({
|
|
|
60
56
|
DeviceConfigFile().remove();
|
|
61
57
|
|
|
62
58
|
await safeRimraf(CREDENTIALS_FILE_PATH);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
|
|
60
|
+
if (existsSync(APP_ROOT)) {
|
|
61
|
+
const files = await readdir(APP_ROOT);
|
|
62
|
+
logger.debug(
|
|
63
|
+
`${APP_ROOT} folder contents ${JSON.stringify(files.length)}`
|
|
64
|
+
);
|
|
65
|
+
// This won't handle hidden files, but will remove if truly empty, which is safer
|
|
66
|
+
if (files.length === 0) {
|
|
67
|
+
logger.debug('Applications directory is empty, removing.');
|
|
68
|
+
await safeRimraf(APP_ROOT);
|
|
69
|
+
}
|
|
70
|
+
logger.info('Device configuration cleaned');
|
|
69
71
|
}
|
|
70
|
-
logger.info('Device configuration cleaned');
|
|
71
72
|
}
|
|
72
73
|
});
|
|
@@ -5,6 +5,7 @@ import { initCliLeaf } from './init';
|
|
|
5
5
|
import { restartCliLeaf } from './restart';
|
|
6
6
|
import { refreshCliLeaf } from './refresh';
|
|
7
7
|
import { migrateCliLeaf } from './migrate';
|
|
8
|
+
import { stopLocalConnectionCliLeaf } from './local-connection';
|
|
8
9
|
|
|
9
10
|
export const deviceCliBranch = CliBranch({
|
|
10
11
|
name: 'device',
|
|
@@ -15,6 +16,7 @@ export const deviceCliBranch = CliBranch({
|
|
|
15
16
|
refreshCliLeaf,
|
|
16
17
|
cleanCliLeaf,
|
|
17
18
|
restartCliLeaf,
|
|
18
|
-
migrateCliLeaf
|
|
19
|
+
migrateCliLeaf,
|
|
20
|
+
stopLocalConnectionCliLeaf
|
|
19
21
|
]
|
|
20
22
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CliLeaf } from '@alwaysai/alwayscli';
|
|
2
|
+
import { stopRabbitMQContainer } from '../../local-connection/rabbitmq-container';
|
|
3
|
+
import { logger } from '../../util/logger';
|
|
4
|
+
import { getDeviceAgentDockerComposePath } from '../../util/directories';
|
|
5
|
+
|
|
6
|
+
export const stopLocalConnectionCliLeaf = CliLeaf({
|
|
7
|
+
name: 'stop-local-connection',
|
|
8
|
+
description: 'Stop the Device Agent local connection container',
|
|
9
|
+
async action(_, opts) {
|
|
10
|
+
if ((await stopRabbitMQContainer()) === false) {
|
|
11
|
+
logger.error(
|
|
12
|
+
`You may need to manually stop the container by running docker-compose down in the following directory: ${getDeviceAgentDockerComposePath()}`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
});
|