1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const crypto = require("crypto");
|
4 | const _ = require("underscore");
|
5 | const os = require("os");
|
6 | const path = require("path");
|
7 | const request = require("request-promise");
|
8 | const docker_cli_js_1 = require("docker-cli-js");
|
9 | const cron_1 = require("cron");
|
10 | const uuid = require("uuid/v5");
|
11 | const sbase = require("@nodeswork/sbase");
|
12 | const logger = require("@nodeswork/logger");
|
13 | const utils_1 = require("@nodeswork/utils");
|
14 | const applet = require("@nodeswork/applet");
|
15 | const errors = require("./errors");
|
16 | const utils_2 = require("./utils");
|
17 | const server_1 = require("./server");
|
18 | const socket_1 = require("./socket");
|
19 | const paths_1 = require("./paths");
|
20 | const compareVersion = require("compare-version");
|
21 | const latestVersion = require('latest-version');
|
22 | const LOG = logger.getLogger();
|
23 | const APPLET_MANAGER_KEY = 'appletManager';
|
24 | const containerVersion = require('../package.json').version;
|
25 | const isRunning = require('is-running');
|
26 | const machineId = require('node-machine-id').machineIdSync;
|
27 | const UUID_NAMESPACE = '5daabcd8-f17e-568c-aa6f-da9d92c7032c';
|
28 | const NAME_REGEX = /^na-(\w+)-([0-9.]+)-([\w-]+)_([0-9.]+)-(\w+)$/;
|
29 | class AppletManager {
|
30 | constructor(options) {
|
31 | this.options = options;
|
32 | this.docker = new docker_cli_js_1.Docker();
|
33 | this.cronJobs = [];
|
34 | if (this.options.debug) {
|
35 | LOG.level = 'debug';
|
36 | }
|
37 | const configPath = path.join(options.appPath, 'config.json');
|
38 | LOG.debug('Load configuration from configPath', configPath);
|
39 | this.ls = utils_2.localStorage(configPath);
|
40 | let amOptions = this.ls.getItemSync(APPLET_MANAGER_KEY);
|
41 | let running = false;
|
42 | if (amOptions == null) {
|
43 | amOptions = this.options;
|
44 | LOG.debug('Initialize Applet Manager Options to local:', amOptions);
|
45 | this.ls.setItemSync(APPLET_MANAGER_KEY, amOptions);
|
46 | }
|
47 | else {
|
48 | LOG.debug('Got Applet Manager Options from local:', amOptions);
|
49 | if (amOptions.pid) {
|
50 | running = isRunning(amOptions.pid);
|
51 | }
|
52 | this.options.token = amOptions.token;
|
53 | }
|
54 | if (running && (this.options.appPath !== amOptions.appPath ||
|
55 | this.options.nodesworkServer !== amOptions.nodesworkServer ||
|
56 | this.options.port !== amOptions.port)) {
|
57 | throw new utils_1.NodesworkError('Configuration does not match with existing Applet Manager', {
|
58 | newOption: this.options,
|
59 | runningOption: _.pick(amOptions, 'appPath', 'nodesworkServer', 'port'),
|
60 | });
|
61 | }
|
62 | if (running) {
|
63 | this.options.pid = amOptions.pid;
|
64 | this.options.token = amOptions.token;
|
65 | }
|
66 | else {
|
67 | this.options.pid = null;
|
68 | }
|
69 | this.serverApi = request.defaults({
|
70 | headers: {
|
71 | 'device-token': this.options.token,
|
72 | },
|
73 | baseUrl: this.options.nodesworkServer,
|
74 | json: true,
|
75 | jar: true,
|
76 | });
|
77 | }
|
78 | authenticated() {
|
79 | return this.options.token != null;
|
80 | }
|
81 | |
82 |
|
83 |
|
84 |
|
85 |
|
86 | async authenticate(options) {
|
87 | try {
|
88 | const resp = await request.post({
|
89 | baseUrl: this.options.nodesworkServer,
|
90 | uri: '/v1/u/user/login',
|
91 | body: {
|
92 | email: options.email,
|
93 | password: options.password,
|
94 | },
|
95 | json: true,
|
96 | jar: true,
|
97 | });
|
98 | LOG.debug('Login successfully');
|
99 | }
|
100 | catch (e) {
|
101 | if (e.name === 'RequestError') {
|
102 | throw new utils_1.NodesworkError('Server is not available', {
|
103 | path: this.options.nodesworkServer + '/v1/u/user/login',
|
104 | }, e);
|
105 | }
|
106 | else if (e.statusCode === 401) {
|
107 | throw new utils_1.NodesworkError('Wrong password');
|
108 | }
|
109 | else if (e.statusCode === 422) {
|
110 | throw new utils_1.NodesworkError('Wrong email');
|
111 | }
|
112 | else {
|
113 | throw e;
|
114 | }
|
115 | }
|
116 | try {
|
117 | const mid = machineId();
|
118 | LOG.debug('Got machine id:', mid);
|
119 | const deviceIdentifier = crypto.createHash('md5')
|
120 | .update(mid)
|
121 | .update(options.email)
|
122 | .digest('hex');
|
123 | let operatingSystem = {
|
124 | Darwin: 'MacOS',
|
125 | Windows_NT: 'Windows',
|
126 | Linux: 'Linux',
|
127 | }[os.type()];
|
128 | const device = {
|
129 | deviceType: 'UserDevice',
|
130 | deviceIdentifier,
|
131 | os: operatingSystem,
|
132 | osVersion: os.release(),
|
133 | containerVersion,
|
134 | name: options.deviceName,
|
135 | };
|
136 | LOG.debug('Collected device information:', device);
|
137 | const resp = await request.post({
|
138 | baseUrl: this.options.nodesworkServer,
|
139 | uri: '/v1/u/devices',
|
140 | body: device,
|
141 | json: true,
|
142 | jar: true,
|
143 | });
|
144 | LOG.debug('Device registered successfully', resp);
|
145 | this.options.token = resp.token;
|
146 | this.ls.setItemSync(APPLET_MANAGER_KEY, this.options);
|
147 | LOG.debug('Save token to local', this.options);
|
148 | await this.updateDevice();
|
149 | }
|
150 | catch (e) {
|
151 | if (e.name === 'RequestError') {
|
152 | throw new utils_1.NodesworkError('Server is not available', {
|
153 | path: this.options.nodesworkServer + '/v1/u/user/login',
|
154 | }, e);
|
155 | }
|
156 | else {
|
157 | throw e;
|
158 | }
|
159 | }
|
160 | return;
|
161 | }
|
162 | isStarted() {
|
163 | return this.options.pid != null;
|
164 | }
|
165 | |
166 |
|
167 |
|
168 |
|
169 |
|
170 | async startServer() {
|
171 | if (this.options.pid != null) {
|
172 | console.log('daemon has already started');
|
173 | return;
|
174 | }
|
175 | if (this.options.token == null) {
|
176 | throw errors.UNAUTHENTICATED_ERROR;
|
177 | }
|
178 | await this.checkEnvironment();
|
179 |
|
180 | this.options.pid = process.pid;
|
181 | this.ls.setItemSync(APPLET_MANAGER_KEY, this.options);
|
182 | server_1.app.appletManager = this;
|
183 | server_1.server.listen(this.options.port);
|
184 | socket_1.connectSocket(this.options.nodesworkServer, this.options.token, this);
|
185 | LOG.info(`Server is started at http://localhost:${this.options.port}`);
|
186 | await this.updateDevice();
|
187 | }
|
188 | |
189 |
|
190 |
|
191 |
|
192 |
|
193 | async stopServer() {
|
194 |
|
195 | if (this.options.pid) {
|
196 | process.kill(this.options.pid);
|
197 | this.options.pid = null;
|
198 | await utils_2.sleep(1000);
|
199 | }
|
200 | }
|
201 | async install(options) {
|
202 | const docker = new docker_cli_js_1.Docker();
|
203 | const cmd = `build -t ${imageName(options)} --build-arg package=${options.packageName} --build-arg version=${options.version} docker/${options.naType}/${options.naVersion}`;
|
204 | LOG.debug('Execute command to install applet', { cmd });
|
205 | try {
|
206 | const result = await docker.command(cmd);
|
207 | LOG.debug('Execute build command log', result);
|
208 | await this.updateDevice();
|
209 | }
|
210 | catch (e) {
|
211 | throw e;
|
212 | }
|
213 | }
|
214 | async images() {
|
215 | const docker = new docker_cli_js_1.Docker();
|
216 | const images = await docker.command('images');
|
217 | return _.chain(images.images)
|
218 | .filter((image) => {
|
219 | const [na, naType, naVersion, ...others] = image.repository.split('-');
|
220 | return na === 'na' && (naType === 'npm') && (naVersion === '8.3.0');
|
221 | })
|
222 | .map((image) => {
|
223 | const [na, naType, naVersion, ...others] = image.repository.split('-');
|
224 | return {
|
225 | naType,
|
226 | naVersion,
|
227 | packageName: others.join('-'),
|
228 | version: image.tag,
|
229 | };
|
230 | })
|
231 | .value();
|
232 | }
|
233 | async run(options) {
|
234 | await this.checkEnvironment();
|
235 | const uniqueName = this.name(options);
|
236 | const rmCmd = `rm ${uniqueName}`;
|
237 | LOG.debug('Execute command to rm applet', { cmd: rmCmd });
|
238 | try {
|
239 | const docker = new docker_cli_js_1.Docker();
|
240 | const result = await docker.command(rmCmd);
|
241 | LOG.debug('Execute build command log', result);
|
242 | }
|
243 | catch (e) {
|
244 | LOG.debug('Container does not exist');
|
245 | }
|
246 | const image = imageName(options);
|
247 | const cmd = `run --name ${uniqueName} --network nodeswork -d -e ${applet.constants.environmentKeys.APPLET_ID}=${options.appletId} -e ${applet.constants.environmentKeys.APPLET_TOKEN}=${options.appletToken} ${image}`;
|
248 | LOG.debug('Execute command to run applet', { cmd });
|
249 | try {
|
250 | const docker = new docker_cli_js_1.Docker();
|
251 | const result = await docker.command(cmd);
|
252 | LOG.debug('Execute run command result', result);
|
253 | await this.updateDevice();
|
254 | }
|
255 | catch (e) {
|
256 | LOG.error('Execute run command failed', e);
|
257 | throw e;
|
258 | }
|
259 | }
|
260 | async kill(options) {
|
261 | const uniqueName = this.name(options);
|
262 | const cmd = `stop ${uniqueName}`;
|
263 | LOG.debug('Execute command to run applet', { cmd });
|
264 | try {
|
265 | const docker = new docker_cli_js_1.Docker();
|
266 | const result = await docker.command(cmd);
|
267 | LOG.debug('Execute build command log', result);
|
268 | await this.updateDevice();
|
269 | }
|
270 | catch (e) {
|
271 | throw e;
|
272 | }
|
273 | }
|
274 | async ps() {
|
275 |
|
276 | const psResult = await this.docker.command('ps');
|
277 | const psApplets = _.chain(psResult.containerList)
|
278 | .map((container) => {
|
279 | const image = parseAppletImage(container.names);
|
280 | if (image == null) {
|
281 | return null;
|
282 | }
|
283 | const port = parseMappingPort(container.ports) || 28900;
|
284 | return _.extend(image, { port, status: container.status });
|
285 | })
|
286 | .filter(_.identity)
|
287 | .value();
|
288 | const networkResults = Object.values((await this.docker.command('network inspect nodeswork')).object[0].Containers);
|
289 | return _.filter(psApplets, (psApplet) => {
|
290 | const appletName = this.name(psApplet);
|
291 | const networkResult = _.find(networkResults, (result) => {
|
292 | return result.Name === appletName;
|
293 | });
|
294 | if (networkResult == null) {
|
295 | LOG.warn(`Applet ${appletName} is running but not in the correct network`);
|
296 | return false;
|
297 | }
|
298 | psApplet.ip = networkResult.IPv4Address.split('/')[0];
|
299 | return true;
|
300 | });
|
301 | }
|
302 | async refreshWorkerCrons() {
|
303 | const self = this;
|
304 | try {
|
305 | const userApplets = await this.serverApi.get({
|
306 | uri: '/v1/d/user-applets',
|
307 | });
|
308 | const newJobs = _
|
309 | .chain(userApplets)
|
310 | .map((ua) => {
|
311 | const appletConfig = ua.config.appletConfig;
|
312 | const image = {
|
313 | naType: appletConfig.naType,
|
314 | naVersion: appletConfig.naVersion,
|
315 | packageName: appletConfig.packageName,
|
316 | version: appletConfig.version,
|
317 | };
|
318 | return _.map(appletConfig.workers, (workerConfig) => {
|
319 | const worker = {
|
320 | handler: workerConfig.handler,
|
321 | name: workerConfig.name,
|
322 | };
|
323 | return {
|
324 | jobUUID: uuid([
|
325 | ua.applet._id,
|
326 | ua._id,
|
327 | image.naType,
|
328 | image.naVersion,
|
329 | image.packageName,
|
330 | image.version,
|
331 | worker.handler,
|
332 | worker.name,
|
333 | ].join(':'), UUID_NAMESPACE),
|
334 | appletId: ua.applet._id,
|
335 | userApplet: ua._id,
|
336 | image,
|
337 | worker,
|
338 | schedule: workerConfig.schedule,
|
339 | };
|
340 | });
|
341 | })
|
342 | .flatten()
|
343 | .filter((x) => x.schedule != null)
|
344 | .value();
|
345 | LOG.debug('Fetch applets for current device successfully', newJobs);
|
346 | for (const cron of this.cronJobs) {
|
347 | const u = _.find(newJobs, (newJob) => newJob.jobUUID === cron.jobUUID);
|
348 | if (u == null) {
|
349 | cron.cronJob.stop();
|
350 | LOG.info('Stop cron job successfully', _.omit(cron, 'cronJob'));
|
351 | }
|
352 | }
|
353 | for (const newJob of newJobs) {
|
354 | const cron = _.find(this.cronJobs, (c) => newJob.jobUUID === c.jobUUID);
|
355 | if (cron == null) {
|
356 | const cronJob = (function (c) {
|
357 | try {
|
358 | c.cronJob = new cron_1.CronJob({
|
359 | cronTime: c.schedule,
|
360 | onTick: async () => {
|
361 | LOG.debug('Run cron job', _.omit(c, 'cronJob'));
|
362 | try {
|
363 | await self.executeCronJob(c);
|
364 | LOG.info('Run cron job successfully', _.omit(c, 'cronJob'));
|
365 | }
|
366 | catch (e) {
|
367 | LOG.error('Run cron job failed', _.pick(e, 'name', 'statusCode', 'error', 'message'), _.omit(c, 'cronJob'));
|
368 | }
|
369 | },
|
370 | start: true,
|
371 | });
|
372 | }
|
373 | catch (e) {
|
374 | LOG.error('Create cron job failed', _.omit(c, 'cronJob'));
|
375 | }
|
376 | LOG.info('Create cron job successfully', _.omit(c, 'cronJob'));
|
377 | return c;
|
378 | })(newJob);
|
379 | this.cronJobs.push(cronJob);
|
380 | }
|
381 | }
|
382 | this.cronJobs = _.filter(this.cronJobs, (cronJob) => {
|
383 | return cronJob.cronJob.running;
|
384 | });
|
385 | }
|
386 | catch (e) {
|
387 | throw e;
|
388 | }
|
389 | }
|
390 | async executeCronJob(job) {
|
391 | try {
|
392 | const accounts = await this.serverApi.get({
|
393 | uri: `/v1/d/user-applets/${job.userApplet}/accounts`,
|
394 | });
|
395 | LOG.debug('Fetch accounts successfully', accounts);
|
396 | const payload = {
|
397 | accounts,
|
398 | };
|
399 | const result = await this.work({
|
400 | userApplet: job.userApplet,
|
401 | route: {
|
402 | appletId: job.appletId,
|
403 | naType: job.image.naType,
|
404 | naVersion: job.image.naVersion,
|
405 | packageName: job.image.packageName,
|
406 | version: job.image.version,
|
407 | },
|
408 | worker: job.worker,
|
409 | payload,
|
410 | });
|
411 | LOG.info('Execute cron job successfully.', {
|
412 | job: _.omit(job, 'cronJob'),
|
413 | result,
|
414 | });
|
415 | }
|
416 | catch (e) {
|
417 | throw e;
|
418 | }
|
419 | }
|
420 | async updateExecutionMetrics(executionId, options) {
|
421 | return await this.serverApi.post({
|
422 | uri: `/v1/d/executions/${executionId}/metrics`,
|
423 | body: {
|
424 | dimensions: options.dimensions,
|
425 | name: options.name,
|
426 | value: options.value,
|
427 | },
|
428 | });
|
429 | }
|
430 | async work(options) {
|
431 | LOG.debug('Get work request', options);
|
432 | const execution = await this.serverApi.post({
|
433 | uri: `/v1/d/user-applets/${options.userApplet}/execute`,
|
434 | body: {
|
435 | worker: options.worker,
|
436 | },
|
437 | });
|
438 | const headers = {};
|
439 | headers[applet.constants.headers.request.EXECUTION_ID] = execution._id;
|
440 | const requestOptions = {
|
441 | appletId: options.route.appletId,
|
442 | naType: options.route.naType,
|
443 | naVersion: options.route.naVersion,
|
444 | packageName: options.route.packageName,
|
445 | version: options.route.version,
|
446 | uri: `/workers/${options.worker.handler}/${options.worker.name}`,
|
447 | method: 'POST',
|
448 | body: options.payload,
|
449 | headers,
|
450 | };
|
451 | try {
|
452 | const result = await this.request(requestOptions);
|
453 | this.updateExecutionMetrics(execution._id, {
|
454 | dimensions: {
|
455 | status: 'SUCCESS',
|
456 | },
|
457 | name: 'result',
|
458 | value: utils_1.metrics.Count(1),
|
459 | });
|
460 | return result;
|
461 | }
|
462 | catch (e) {
|
463 | this.updateExecutionMetrics(execution._id, {
|
464 | dimensions: {
|
465 | status: 'ERROR',
|
466 | },
|
467 | name: 'result',
|
468 | value: utils_1.metrics.Count(1),
|
469 | });
|
470 | throw e;
|
471 | }
|
472 | }
|
473 | async request(options) {
|
474 | LOG.info('Get request', { options });
|
475 | const routeAddress = await this.route(options);
|
476 | if (routeAddress == null) {
|
477 | throw new utils_1.NodesworkError('Applet is not running');
|
478 | }
|
479 | const headers = _.extend({}, options.headers);
|
480 | headers[sbase.constants.headers.request.NODESWORK_FORWARDED_TO] = (routeAddress.route);
|
481 | const requestOptions = {
|
482 | uri: routeAddress.target + options.uri,
|
483 | method: options.method,
|
484 | proxy: routeAddress.target,
|
485 | body: options.body,
|
486 | headers,
|
487 | json: true,
|
488 | };
|
489 | LOG.debug('Request options', requestOptions);
|
490 | const resp = await request(requestOptions);
|
491 | LOG.debug('Request response', resp);
|
492 | return resp;
|
493 | }
|
494 | async operateAccount(options) {
|
495 | const requestOptions = {
|
496 | uri: `/v1/d/applets/${options.appletId}/accounts/${options.accountId}/operate`,
|
497 | body: options.body,
|
498 | };
|
499 | return await this.serverApi.post(requestOptions);
|
500 | }
|
501 | async route(options) {
|
502 | if (this.options.dev) {
|
503 | try {
|
504 | const devServer = await request({
|
505 | uri: 'http://localhost:28900/sstats',
|
506 | json: true,
|
507 | });
|
508 | if (devServer.applet &&
|
509 | devServer.applet.packageName === options.packageName &&
|
510 | compareVersion(devServer.applet.packageVersion, options.version) >= 0) {
|
511 | return {
|
512 | route: 'localhost:28900',
|
513 | target: 'http://localhost:28900',
|
514 | };
|
515 | }
|
516 | }
|
517 | catch (e) {
|
518 |
|
519 | }
|
520 | }
|
521 | return {
|
522 | route: `${this.name(options)}:28900`,
|
523 | target: paths_1.containerProxyUrl,
|
524 | };
|
525 | }
|
526 | async updateDevice() {
|
527 | if (this.options.token == null) {
|
528 | throw errors.UNAUTHENTICATED_ERROR;
|
529 | }
|
530 | const installedApplets = await this.images();
|
531 | const runningApplets = await this.ps();
|
532 | try {
|
533 | const resp = await this.serverApi.post({
|
534 | uri: '/v1/d/devices',
|
535 | body: {
|
536 | installedApplets,
|
537 | runningApplets,
|
538 | },
|
539 | });
|
540 | LOG.debug('Update device successfully');
|
541 | }
|
542 | catch (e) {
|
543 | throw e;
|
544 | }
|
545 | await this.refreshWorkerCrons();
|
546 | }
|
547 | async checkEnvironment() {
|
548 |
|
549 | const networks = await this.docker.command('network ls');
|
550 | const targetNetwork = _.find(networks.network, (c) => c.name === 'nodeswork');
|
551 | if (targetNetwork == null) {
|
552 | LOG.debug('network is not setup, creating');
|
553 | await this.docker.command('network create nodeswork');
|
554 | }
|
555 | const inspect = await this.docker.command('network inspect nodeswork');
|
556 | LOG.debug('inspecting network', inspect.object[0]);
|
557 | const IPAMConfig = inspect.object[0].IPAM.Config[0];
|
558 | this.network = {
|
559 | subnet: IPAMConfig.Subnet,
|
560 | gateway: IPAMConfig.Gateway,
|
561 | containers: inspect.object[0].Containers,
|
562 | };
|
563 | LOG.debug('Network configuration', this.network);
|
564 |
|
565 |
|
566 | const containers = await this.docker.command('ps');
|
567 | const proxyContainer = _.find(containers.containerList, (container) => container.names === 'nodeswork-container-proxy');
|
568 | if (proxyContainer == null) {
|
569 | LOG.debug('Container proxy is not running, starting');
|
570 | await this.installContainerProxy();
|
571 | }
|
572 | else {
|
573 | const version = proxyContainer.image.split(':')[1];
|
574 | const lVersion = await latestVersion('@nodeswork/container-proxy');
|
575 | this.containerProxy = {
|
576 | version,
|
577 | latestVersion: lVersion,
|
578 | };
|
579 | }
|
580 | const proxyInNetwork = _.find(this.network.containers, (container) => {
|
581 | return container.Name === 'nodeswork-container-proxy';
|
582 | });
|
583 | if (proxyInNetwork == null) {
|
584 | LOG.debug('Proxy container is not in network');
|
585 | await this.docker.command(`network connect nodeswork nodeswork-container-proxy`);
|
586 | }
|
587 | LOG.debug('Container Proxy configuration', this.containerProxy);
|
588 | await this.ensureMongo({ prefix: 'nodeswork', port: 28330 });
|
589 | LOG.debug('Environment setup correctly');
|
590 | }
|
591 | async ensureMongo(options) {
|
592 | LOG.debug('Ensure mongo', options);
|
593 | const name = `${options.prefix}-mongo`;
|
594 | let containers = await this.docker.command('ps');
|
595 | let container = _.find(containers.containerList, (c) => {
|
596 | return c.names === name;
|
597 | });
|
598 | if (container != null) {
|
599 | LOG.debug('Mongo is already running');
|
600 | return options.port;
|
601 | }
|
602 | LOG.debug('Mongo is not running');
|
603 | containers = await this.docker.command('ps -a');
|
604 | container = _.find(containers.containerList, (c) => {
|
605 | return c.names === name;
|
606 | });
|
607 | if (container != null) {
|
608 | LOG.debug('Mongo is stopped, starting');
|
609 | await this.docker.command(`start ${name}`);
|
610 | return options.port;
|
611 | }
|
612 | LOG.debug('Start Mongo instance');
|
613 | await this.docker.command(`run --name ${name} -p ${options.port}:27017 -d mongo`);
|
614 | LOG.debug('Mongo is running');
|
615 | return options.port;
|
616 | }
|
617 | name(options) {
|
618 | return `na-${options.naType}-${options.naVersion}-${options.packageName}_${options.version}-${options.appletId}`;
|
619 | }
|
620 | async installContainerProxy() {
|
621 | const version = await latestVersion('@nodeswork/container-proxy');
|
622 | LOG.debug('Fetched latest version container-proxy', { version });
|
623 | const output = await this.docker.command(`build -t nodeswork-container-proxy:${version} docker/container-proxy --build-arg version=${version}`);
|
624 | LOG.debug('Building container proxy', output);
|
625 | try {
|
626 | await this.docker.command(`rm nodeswork-container-proxy`);
|
627 | }
|
628 | catch (e) {
|
629 | LOG.debug('Remove container proxy error', e);
|
630 | }
|
631 | try {
|
632 |
|
633 | await this.docker.command(`run --name nodeswork-container-proxy -d -e NAM_HOST=172.16.222.111:28310 -e SUB_NET=${this.network.subnet} -p 28320:28320 nodeswork-container-proxy:${version}`);
|
634 | }
|
635 | catch (e) {
|
636 | LOG.debug('Remove container proxy error', e);
|
637 | }
|
638 | this.containerProxy = { version, latestVersion: version };
|
639 | }
|
640 | }
|
641 | exports.AppletManager = AppletManager;
|
642 | function imageName(image) {
|
643 | return `na-${image.naType}-${image.naVersion}-${image.packageName}\
|
644 | :${image.version}`;
|
645 | }
|
646 | function parseAppletImage(name) {
|
647 | const result = NAME_REGEX.exec(name);
|
648 | if (result == null) {
|
649 | return null;
|
650 | }
|
651 | return {
|
652 | naType: result[1],
|
653 | naVersion: result[2],
|
654 | packageName: result[3],
|
655 | version: result[4],
|
656 | appletId: result[5],
|
657 | };
|
658 | }
|
659 | function parseMappingPort(ports) {
|
660 | return parseInt(ports.split(':')[1]);
|
661 | }
|
662 |
|
663 |
|