@kapeta/local-cluster-service 0.7.2 → 0.7.4

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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.7.4](https://github.com/kapetacom/local-cluster-service/compare/v0.7.3...v0.7.4) (2023-07-17)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Add host.docker.internal on Linux ([#43](https://github.com/kapetacom/local-cluster-service/issues/43)) ([f76eab1](https://github.com/kapetacom/local-cluster-service/commit/f76eab13059bc920026769303b49cf065d48f5ca))
7
+
8
+ ## [0.7.3](https://github.com/kapetacom/local-cluster-service/compare/v0.7.2...v0.7.3) (2023-07-17)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Handle windows paths ([45eedcb](https://github.com/kapetacom/local-cluster-service/commit/45eedcb4b95a1d403d88dd63d74fa98b2c949559))
14
+
1
15
  ## [0.7.2](https://github.com/kapetacom/local-cluster-service/compare/v0.7.1...v0.7.2) (2023-07-16)
2
16
 
3
17
 
@@ -27,6 +27,7 @@ declare class ContainerManager {
27
27
  private _docker;
28
28
  private _alive;
29
29
  private _mountDir;
30
+ private _version;
30
31
  constructor();
31
32
  initialize(): Promise<void>;
32
33
  isAlive(): boolean;
@@ -85,5 +86,14 @@ export declare class ContainerInfo {
85
86
  getStatus(): Promise<any>;
86
87
  getPorts(): Promise<PortMap | false>;
87
88
  }
89
+ export declare function getExtraHosts(dockerVersion: string): string[] | undefined;
90
+ /**
91
+ * Ensure that the volume is in the correct format for the docker daemon on the host
92
+ *
93
+ * Windows: c:\path\to\volume -> /c/path/to/volume
94
+ * Linux: /path/to/volume -> /path/to/volume
95
+ * Mac: /path/to/volume -> /path/to/volume
96
+ */
97
+ export declare function toLocalBindVolume(volume: string): string;
88
98
  export declare const containerManager: ContainerManager;
89
99
  export {};
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.containerManager = exports.ContainerInfo = void 0;
6
+ exports.containerManager = exports.toLocalBindVolume = exports.getExtraHosts = exports.ContainerInfo = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const storageService_1 = require("./storageService");
9
9
  const os_1 = __importDefault(require("os"));
@@ -27,9 +27,11 @@ class ContainerManager {
27
27
  _docker;
28
28
  _alive;
29
29
  _mountDir;
30
+ _version;
30
31
  constructor() {
31
32
  this._docker = null;
32
33
  this._alive = false;
34
+ this._version = '';
33
35
  this._mountDir = path_1.default.join(storageService_1.storageService.getKapetaBasedir(), 'mounts');
34
36
  fs_extra_1.default.mkdirpSync(this._mountDir);
35
37
  }
@@ -58,7 +60,10 @@ class ContainerManager {
58
60
  const client = new node_docker_api_1.Docker(opts);
59
61
  await client.ping();
60
62
  this._docker = client;
63
+ const versionInfo = await client.version();
64
+ this._version = versionInfo.Server?.Version;
61
65
  this._alive = true;
66
+ console.log('Connected to docker daemon with version: %s', this._version);
62
67
  return;
63
68
  }
64
69
  catch (err) {
@@ -143,7 +148,7 @@ class ContainerManager {
143
148
  lodash_1.default.forEach(mounts, (Source, Target) => {
144
149
  Mounts.push({
145
150
  Target,
146
- Source,
151
+ Source: toLocalBindVolume(Source),
147
152
  Type: 'bind',
148
153
  ReadOnly: false,
149
154
  Consistency: 'consistent',
@@ -205,6 +210,16 @@ class ContainerManager {
205
210
  return new ContainerInfo(dockerContainer);
206
211
  }
207
212
  async startContainer(opts) {
213
+ const extraHosts = getExtraHosts(this._version);
214
+ if (extraHosts && extraHosts.length > 0) {
215
+ if (!opts.HostConfig) {
216
+ opts.HostConfig = {};
217
+ }
218
+ if (!opts.HostConfig.ExtraHosts) {
219
+ opts.HostConfig.ExtraHosts = [];
220
+ }
221
+ opts.HostConfig.ExtraHosts = opts.HostConfig.ExtraHosts.concat(extraHosts);
222
+ }
208
223
  const dockerContainer = await this.docker().container.create(opts);
209
224
  await dockerContainer.start();
210
225
  return dockerContainer;
@@ -362,4 +377,37 @@ class ContainerInfo {
362
377
  }
363
378
  }
364
379
  exports.ContainerInfo = ContainerInfo;
380
+ function getExtraHosts(dockerVersion) {
381
+ if (process.platform !== 'darwin' && process.platform !== 'win32') {
382
+ const [major, minor] = dockerVersion.split('.');
383
+ if (parseInt(major) >= 20 && parseInt(minor) >= 10) {
384
+ // Docker 20.10+ on Linux supports adding host.docker.internal to point to host-gateway
385
+ return ['host.docker.internal:host-gateway'];
386
+ }
387
+ // Docker versions lower than 20.10 needs an actual IP address. We use the default network bridge which
388
+ // is always 172.17.0.1
389
+ return ['host.docker.internal:172.17.0.1'];
390
+ }
391
+ return undefined;
392
+ }
393
+ exports.getExtraHosts = getExtraHosts;
394
+ /**
395
+ * Ensure that the volume is in the correct format for the docker daemon on the host
396
+ *
397
+ * Windows: c:\path\to\volume -> /c/path/to/volume
398
+ * Linux: /path/to/volume -> /path/to/volume
399
+ * Mac: /path/to/volume -> /path/to/volume
400
+ */
401
+ function toLocalBindVolume(volume) {
402
+ if (process.platform === 'win32') {
403
+ //On Windows we need to convert c:\ to /c/
404
+ return volume
405
+ .replace(/^([a-z]):\\/i, (match, drive) => {
406
+ return '/' + drive.toLowerCase() + '/';
407
+ })
408
+ .replace(/\\(\S)/g, '/$1');
409
+ }
410
+ return volume;
411
+ }
412
+ exports.toLocalBindVolume = toLocalBindVolume;
365
413
  exports.containerManager = new ContainerManager();
@@ -18,7 +18,6 @@ const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
18
18
  const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
19
19
  const KAPETA_BLOCK_REF = 'KAPETA_BLOCK_REF';
20
20
  const KAPETA_INSTANCE_ID = 'KAPETA_INSTANCE_ID';
21
- const KAPETA_LOCAL_CLUSTER_PORT = 'KAPETA_LOCAL_CLUSTER_PORT';
22
21
  /**
23
22
  * Needed when running local docker containers as part of plan
24
23
  * @type {string[]}
@@ -204,8 +203,8 @@ class BlockInstanceRunner {
204
203
  ],
205
204
  HostConfig: {
206
205
  Binds: [
207
- `${local_cluster_config_1.default.getKapetaBasedir()}:${homeDir}/.kapeta`,
208
- `${baseDir}:${workingDir}`, //We mount
206
+ `${(0, containerManager_1.toLocalBindVolume)(local_cluster_config_1.default.getKapetaBasedir())}:${homeDir}/.kapeta`,
207
+ `${(0, containerManager_1.toLocalBindVolume)(baseDir)}:${workingDir}`, //We mount
209
208
  ],
210
209
  PortBindings,
211
210
  },
@@ -315,7 +314,9 @@ class BlockInstanceRunner {
315
314
  ...Object.entries(env).map(([key, value]) => `${key}=${value}`),
316
315
  ],
317
316
  HostConfig: {
318
- Binds: [`${local_cluster_config_1.default.getKapetaBasedir()}:${local_cluster_config_1.default.getKapetaBasedir()}`],
317
+ Binds: [
318
+ `${(0, containerManager_1.toLocalBindVolume)(local_cluster_config_1.default.getKapetaBasedir())}:${local_cluster_config_1.default.getKapetaBasedir()}`,
319
+ ],
319
320
  },
320
321
  });
321
322
  try {
@@ -426,8 +427,8 @@ class BlockInstanceRunner {
426
427
  HealthCheck,
427
428
  HostConfig: {
428
429
  Binds: [
429
- `${kapetaYmlPath}:/kapeta.yml:ro`,
430
- `${local_cluster_config_1.default.getKapetaBasedir()}:${local_cluster_config_1.default.getKapetaBasedir()}`,
430
+ `${(0, containerManager_1.toLocalBindVolume)(kapetaYmlPath)}:/kapeta.yml:ro`,
431
+ `${(0, containerManager_1.toLocalBindVolume)(local_cluster_config_1.default.getKapetaBasedir())}:${local_cluster_config_1.default.getKapetaBasedir()}`,
431
432
  ],
432
433
  PortBindings,
433
434
  Mounts,
@@ -27,6 +27,7 @@ declare class ContainerManager {
27
27
  private _docker;
28
28
  private _alive;
29
29
  private _mountDir;
30
+ private _version;
30
31
  constructor();
31
32
  initialize(): Promise<void>;
32
33
  isAlive(): boolean;
@@ -85,5 +86,14 @@ export declare class ContainerInfo {
85
86
  getStatus(): Promise<any>;
86
87
  getPorts(): Promise<PortMap | false>;
87
88
  }
89
+ export declare function getExtraHosts(dockerVersion: string): string[] | undefined;
90
+ /**
91
+ * Ensure that the volume is in the correct format for the docker daemon on the host
92
+ *
93
+ * Windows: c:\path\to\volume -> /c/path/to/volume
94
+ * Linux: /path/to/volume -> /path/to/volume
95
+ * Mac: /path/to/volume -> /path/to/volume
96
+ */
97
+ export declare function toLocalBindVolume(volume: string): string;
88
98
  export declare const containerManager: ContainerManager;
89
99
  export {};
@@ -21,9 +21,11 @@ class ContainerManager {
21
21
  _docker;
22
22
  _alive;
23
23
  _mountDir;
24
+ _version;
24
25
  constructor() {
25
26
  this._docker = null;
26
27
  this._alive = false;
28
+ this._version = '';
27
29
  this._mountDir = Path.join(storageService.getKapetaBasedir(), 'mounts');
28
30
  FSExtra.mkdirpSync(this._mountDir);
29
31
  }
@@ -52,7 +54,10 @@ class ContainerManager {
52
54
  const client = new Docker(opts);
53
55
  await client.ping();
54
56
  this._docker = client;
57
+ const versionInfo = await client.version();
58
+ this._version = versionInfo.Server?.Version;
55
59
  this._alive = true;
60
+ console.log('Connected to docker daemon with version: %s', this._version);
56
61
  return;
57
62
  }
58
63
  catch (err) {
@@ -137,7 +142,7 @@ class ContainerManager {
137
142
  _.forEach(mounts, (Source, Target) => {
138
143
  Mounts.push({
139
144
  Target,
140
- Source,
145
+ Source: toLocalBindVolume(Source),
141
146
  Type: 'bind',
142
147
  ReadOnly: false,
143
148
  Consistency: 'consistent',
@@ -199,6 +204,16 @@ class ContainerManager {
199
204
  return new ContainerInfo(dockerContainer);
200
205
  }
201
206
  async startContainer(opts) {
207
+ const extraHosts = getExtraHosts(this._version);
208
+ if (extraHosts && extraHosts.length > 0) {
209
+ if (!opts.HostConfig) {
210
+ opts.HostConfig = {};
211
+ }
212
+ if (!opts.HostConfig.ExtraHosts) {
213
+ opts.HostConfig.ExtraHosts = [];
214
+ }
215
+ opts.HostConfig.ExtraHosts = opts.HostConfig.ExtraHosts.concat(extraHosts);
216
+ }
202
217
  const dockerContainer = await this.docker().container.create(opts);
203
218
  await dockerContainer.start();
204
219
  return dockerContainer;
@@ -355,4 +370,35 @@ export class ContainerInfo {
355
370
  return ports;
356
371
  }
357
372
  }
373
+ export function getExtraHosts(dockerVersion) {
374
+ if (process.platform !== 'darwin' && process.platform !== 'win32') {
375
+ const [major, minor] = dockerVersion.split('.');
376
+ if (parseInt(major) >= 20 && parseInt(minor) >= 10) {
377
+ // Docker 20.10+ on Linux supports adding host.docker.internal to point to host-gateway
378
+ return ['host.docker.internal:host-gateway'];
379
+ }
380
+ // Docker versions lower than 20.10 needs an actual IP address. We use the default network bridge which
381
+ // is always 172.17.0.1
382
+ return ['host.docker.internal:172.17.0.1'];
383
+ }
384
+ return undefined;
385
+ }
386
+ /**
387
+ * Ensure that the volume is in the correct format for the docker daemon on the host
388
+ *
389
+ * Windows: c:\path\to\volume -> /c/path/to/volume
390
+ * Linux: /path/to/volume -> /path/to/volume
391
+ * Mac: /path/to/volume -> /path/to/volume
392
+ */
393
+ export function toLocalBindVolume(volume) {
394
+ if (process.platform === 'win32') {
395
+ //On Windows we need to convert c:\ to /c/
396
+ return volume
397
+ .replace(/^([a-z]):\\/i, (match, drive) => {
398
+ return '/' + drive.toLowerCase() + '/';
399
+ })
400
+ .replace(/\\(\S)/g, '/$1');
401
+ }
402
+ return volume;
403
+ }
358
404
  export const containerManager = new ContainerManager();
@@ -3,7 +3,7 @@ import ClusterConfig from '@kapeta/local-cluster-config';
3
3
  import { readYML } from './utils';
4
4
  import { parseKapetaUri } from '@kapeta/nodejs-utils';
5
5
  import { serviceManager } from '../serviceManager';
6
- import { containerManager } from '../containerManager';
6
+ import { containerManager, toLocalBindVolume } from '../containerManager';
7
7
  import { LogData } from './LogData';
8
8
  import EventEmitter from 'events';
9
9
  import md5 from 'md5';
@@ -12,7 +12,6 @@ const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
12
12
  const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
13
13
  const KAPETA_BLOCK_REF = 'KAPETA_BLOCK_REF';
14
14
  const KAPETA_INSTANCE_ID = 'KAPETA_INSTANCE_ID';
15
- const KAPETA_LOCAL_CLUSTER_PORT = 'KAPETA_LOCAL_CLUSTER_PORT';
16
15
  /**
17
16
  * Needed when running local docker containers as part of plan
18
17
  * @type {string[]}
@@ -198,8 +197,8 @@ export class BlockInstanceRunner {
198
197
  ],
199
198
  HostConfig: {
200
199
  Binds: [
201
- `${ClusterConfig.getKapetaBasedir()}:${homeDir}/.kapeta`,
202
- `${baseDir}:${workingDir}`, //We mount
200
+ `${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${homeDir}/.kapeta`,
201
+ `${toLocalBindVolume(baseDir)}:${workingDir}`, //We mount
203
202
  ],
204
203
  PortBindings,
205
204
  },
@@ -309,7 +308,9 @@ export class BlockInstanceRunner {
309
308
  ...Object.entries(env).map(([key, value]) => `${key}=${value}`),
310
309
  ],
311
310
  HostConfig: {
312
- Binds: [`${ClusterConfig.getKapetaBasedir()}:${ClusterConfig.getKapetaBasedir()}`],
311
+ Binds: [
312
+ `${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${ClusterConfig.getKapetaBasedir()}`,
313
+ ],
313
314
  },
314
315
  });
315
316
  try {
@@ -420,8 +421,8 @@ export class BlockInstanceRunner {
420
421
  HealthCheck,
421
422
  HostConfig: {
422
423
  Binds: [
423
- `${kapetaYmlPath}:/kapeta.yml:ro`,
424
- `${ClusterConfig.getKapetaBasedir()}:${ClusterConfig.getKapetaBasedir()}`,
424
+ `${toLocalBindVolume(kapetaYmlPath)}:/kapeta.yml:ro`,
425
+ `${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${ClusterConfig.getKapetaBasedir()}`,
425
426
  ],
426
427
  PortBindings,
427
428
  Mounts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -51,10 +51,12 @@ class ContainerManager {
51
51
  private _docker: Docker | null;
52
52
  private _alive: boolean;
53
53
  private _mountDir: string;
54
+ private _version: string;
54
55
 
55
56
  constructor() {
56
57
  this._docker = null;
57
58
  this._alive = false;
59
+ this._version = '';
58
60
  this._mountDir = Path.join(storageService.getKapetaBasedir(), 'mounts');
59
61
  FSExtra.mkdirpSync(this._mountDir);
60
62
  }
@@ -85,7 +87,10 @@ class ContainerManager {
85
87
  const client = new Docker(opts);
86
88
  await client.ping();
87
89
  this._docker = client;
90
+ const versionInfo: any = await client.version();
91
+ this._version = versionInfo.Server?.Version;
88
92
  this._alive = true;
93
+ console.log('Connected to docker daemon with version: %s', this._version);
89
94
  return;
90
95
  } catch (err) {
91
96
  // silently ignore bad configs
@@ -189,7 +194,7 @@ class ContainerManager {
189
194
  _.forEach(mounts, (Source, Target) => {
190
195
  Mounts.push({
191
196
  Target,
192
- Source,
197
+ Source: toLocalBindVolume(Source),
193
198
  Type: 'bind',
194
199
  ReadOnly: false,
195
200
  Consistency: 'consistent',
@@ -252,7 +257,6 @@ class ContainerManager {
252
257
  Hostname: name + '.kapeta',
253
258
  Labels,
254
259
  Cmd: opts.cmd,
255
-
256
260
  ExposedPorts,
257
261
  Env,
258
262
  HealthCheck,
@@ -270,6 +274,20 @@ class ContainerManager {
270
274
  }
271
275
 
272
276
  async startContainer(opts: any) {
277
+ const extraHosts = getExtraHosts(this._version);
278
+
279
+ if (extraHosts && extraHosts.length > 0) {
280
+ if (!opts.HostConfig) {
281
+ opts.HostConfig = {};
282
+ }
283
+
284
+ if (!opts.HostConfig.ExtraHosts) {
285
+ opts.HostConfig.ExtraHosts = [];
286
+ }
287
+
288
+ opts.HostConfig.ExtraHosts = opts.HostConfig.ExtraHosts.concat(extraHosts);
289
+ }
290
+
273
291
  const dockerContainer = await this.docker().container.create(opts);
274
292
  await dockerContainer.start();
275
293
  return dockerContainer;
@@ -463,4 +481,38 @@ export class ContainerInfo {
463
481
  }
464
482
  }
465
483
 
484
+ export function getExtraHosts(dockerVersion: string): string[] | undefined {
485
+ if (process.platform !== 'darwin' && process.platform !== 'win32') {
486
+ const [major, minor] = dockerVersion.split('.');
487
+ if (parseInt(major) >= 20 && parseInt(minor) >= 10) {
488
+ // Docker 20.10+ on Linux supports adding host.docker.internal to point to host-gateway
489
+ return ['host.docker.internal:host-gateway'];
490
+ }
491
+ // Docker versions lower than 20.10 needs an actual IP address. We use the default network bridge which
492
+ // is always 172.17.0.1
493
+ return ['host.docker.internal:172.17.0.1'];
494
+ }
495
+
496
+ return undefined;
497
+ }
498
+
499
+ /**
500
+ * Ensure that the volume is in the correct format for the docker daemon on the host
501
+ *
502
+ * Windows: c:\path\to\volume -> /c/path/to/volume
503
+ * Linux: /path/to/volume -> /path/to/volume
504
+ * Mac: /path/to/volume -> /path/to/volume
505
+ */
506
+ export function toLocalBindVolume(volume: string): string {
507
+ if (process.platform === 'win32') {
508
+ //On Windows we need to convert c:\ to /c/
509
+ return volume
510
+ .replace(/^([a-z]):\\/i, (match, drive) => {
511
+ return '/' + drive.toLowerCase() + '/';
512
+ })
513
+ .replace(/\\(\S)/g, '/$1');
514
+ }
515
+ return volume;
516
+ }
517
+
466
518
  export const containerManager = new ContainerManager();
@@ -3,7 +3,7 @@ import ClusterConfig, { DefinitionInfo } from '@kapeta/local-cluster-config';
3
3
  import { readYML } from './utils';
4
4
  import { KapetaURI, parseKapetaUri } from '@kapeta/nodejs-utils';
5
5
  import { serviceManager } from '../serviceManager';
6
- import { containerManager, DockerMounts } from '../containerManager';
6
+ import { containerManager, DockerMounts, toLocalBindVolume } from '../containerManager';
7
7
  import { LogData } from './LogData';
8
8
  import EventEmitter from 'events';
9
9
  import md5 from 'md5';
@@ -15,7 +15,7 @@ const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
15
15
  const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
16
16
  const KAPETA_BLOCK_REF = 'KAPETA_BLOCK_REF';
17
17
  const KAPETA_INSTANCE_ID = 'KAPETA_INSTANCE_ID';
18
- const KAPETA_LOCAL_CLUSTER_PORT = 'KAPETA_LOCAL_CLUSTER_PORT';
18
+
19
19
  /**
20
20
  * Needed when running local docker containers as part of plan
21
21
  * @type {string[]}
@@ -247,8 +247,8 @@ export class BlockInstanceRunner {
247
247
  ],
248
248
  HostConfig: {
249
249
  Binds: [
250
- `${ClusterConfig.getKapetaBasedir()}:${homeDir}/.kapeta`,
251
- `${baseDir}:${workingDir}`, //We mount
250
+ `${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${homeDir}/.kapeta`,
251
+ `${toLocalBindVolume(baseDir)}:${workingDir}`, //We mount
252
252
  ],
253
253
  PortBindings,
254
254
  },
@@ -362,6 +362,7 @@ export class BlockInstanceRunner {
362
362
  }
363
363
  } else {
364
364
  logs.addLog(`Creating new container for block: ${containerName}`);
365
+
365
366
  container = await containerManager.startContainer({
366
367
  Image: dockerImage,
367
368
  name: containerName,
@@ -374,7 +375,9 @@ export class BlockInstanceRunner {
374
375
  ...Object.entries(env).map(([key, value]) => `${key}=${value}`),
375
376
  ],
376
377
  HostConfig: {
377
- Binds: [`${ClusterConfig.getKapetaBasedir()}:${ClusterConfig.getKapetaBasedir()}`],
378
+ Binds: [
379
+ `${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${ClusterConfig.getKapetaBasedir()}`,
380
+ ],
378
381
  },
379
382
  });
380
383
 
@@ -508,8 +511,8 @@ export class BlockInstanceRunner {
508
511
  HealthCheck,
509
512
  HostConfig: {
510
513
  Binds: [
511
- `${kapetaYmlPath}:/kapeta.yml:ro`,
512
- `${ClusterConfig.getKapetaBasedir()}:${ClusterConfig.getKapetaBasedir()}`,
514
+ `${toLocalBindVolume(kapetaYmlPath)}:/kapeta.yml:ro`,
515
+ `${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${ClusterConfig.getKapetaBasedir()}`,
513
516
  ],
514
517
  PortBindings,
515
518
  Mounts,