@mastra/docker 0.1.0 → 0.2.0
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 +46 -0
- package/dist/index.cjs +249 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +249 -1
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +25 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/sandbox/index.d.ts +46 -0
- package/dist/sandbox/index.d.ts.map +1 -1
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# @mastra/docker
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Docker sandbox containers now support resource limits and security hardening through Docker HostConfig options. Configure memory, CPU quota, process IDs, capabilities, security options, read-only root filesystems, and tmpfs mounts. ([#16577](https://github.com/mastra-ai/mastra/pull/16577))
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
const sandbox = new DockerSandbox({
|
|
11
|
+
memory: 512 * 1024 * 1024,
|
|
12
|
+
memorySwap: 512 * 1024 * 1024,
|
|
13
|
+
cpuQuota: 100_000,
|
|
14
|
+
pidsLimit: 256,
|
|
15
|
+
readonlyRootfs: true,
|
|
16
|
+
capDrop: ['ALL'],
|
|
17
|
+
securityOpt: ['no-new-privileges:true'],
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [[`20787de`](https://github.com/mastra-ai/mastra/commit/20787de5965234a1af28fe35f49437c537dbfa0d), [`784ad98`](https://github.com/mastra-ai/mastra/commit/784ad989549de91dc5d33ab8ef36caa6f7dcd34e), [`fceae1f`](https://github.com/mastra-ai/mastra/commit/fceae1f5f5db4722cb078a663c6eb4bd22944123), [`090a647`](https://github.com/mastra-ai/mastra/commit/090a647ba5a66d36f203f9f49457e03a1ff4e6fb), [`bf02acb`](https://github.com/mastra-ai/mastra/commit/bf02acbb8a6110f638ac844e89f1ebf04cb7fe74), [`090a647`](https://github.com/mastra-ai/mastra/commit/090a647ba5a66d36f203f9f49457e03a1ff4e6fb), [`bdb4cbf`](https://github.com/mastra-ai/mastra/commit/bdb4cbf8ba4b685d7481f28bb9dc3de6c79c9ed2), [`0fd3fbe`](https://github.com/mastra-ai/mastra/commit/0fd3fbe40fb63657aedd72f6e7b38c8e8ee6940d), [`f84447d`](https://github.com/mastra-ai/mastra/commit/f84447d6c80f3471836a9b300d246b331fb47e0d), [`a1a5b3e`](https://github.com/mastra-ai/mastra/commit/a1a5b3e42ab2ca5161ea21db59ebf28442680fa7), [`af84f57`](https://github.com/mastra-ai/mastra/commit/af84f571ed762e92e8e61c5f9a72363520914274), [`8b3c6f9`](https://github.com/mastra-ai/mastra/commit/8b3c6f90f7879833ba7d1bc70937e1d8f69d0804), [`fed0475`](https://github.com/mastra-ai/mastra/commit/fed0475ccfea31e4fc251469ac05640d0742c1f0), [`0d53730`](https://github.com/mastra-ai/mastra/commit/0d53730c1ed87ef80c87caa5701c4170ea8028e6), [`522f44d`](https://github.com/mastra-ai/mastra/commit/522f44d947214bfc06cff50599bae1ef3494880d)]:
|
|
24
|
+
- @mastra/core@1.34.0
|
|
25
|
+
|
|
26
|
+
## 0.2.0-alpha.0
|
|
27
|
+
|
|
28
|
+
### Minor Changes
|
|
29
|
+
|
|
30
|
+
- Docker sandbox containers now support resource limits and security hardening through Docker HostConfig options. Configure memory, CPU quota, process IDs, capabilities, security options, read-only root filesystems, and tmpfs mounts. ([#16577](https://github.com/mastra-ai/mastra/pull/16577))
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
const sandbox = new DockerSandbox({
|
|
34
|
+
memory: 512 * 1024 * 1024,
|
|
35
|
+
memorySwap: 512 * 1024 * 1024,
|
|
36
|
+
cpuQuota: 100_000,
|
|
37
|
+
pidsLimit: 256,
|
|
38
|
+
readonlyRootfs: true,
|
|
39
|
+
capDrop: ['ALL'],
|
|
40
|
+
securityOpt: ['no-new-privileges:true'],
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- Updated dependencies [[`fceae1f`](https://github.com/mastra-ai/mastra/commit/fceae1f5f5db4722cb078a663c6eb4bd22944123), [`bf02acb`](https://github.com/mastra-ai/mastra/commit/bf02acbb8a6110f638ac844e89f1ebf04cb7fe74), [`0fd3fbe`](https://github.com/mastra-ai/mastra/commit/0fd3fbe40fb63657aedd72f6e7b38c8e8ee6940d), [`fed0475`](https://github.com/mastra-ai/mastra/commit/fed0475ccfea31e4fc251469ac05640d0742c1f0), [`522f44d`](https://github.com/mastra-ai/mastra/commit/522f44d947214bfc06cff50599bae1ef3494880d)]:
|
|
47
|
+
- @mastra/core@1.34.0-alpha.1
|
|
48
|
+
|
|
3
49
|
## 0.1.0
|
|
4
50
|
|
|
5
51
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var util = require('util');
|
|
3
4
|
var workspace = require('@mastra/core/workspace');
|
|
4
5
|
var Docker = require('dockerode');
|
|
5
6
|
|
|
@@ -275,6 +276,19 @@ var DockerSandbox = class extends workspace.MastraSandbox {
|
|
|
275
276
|
_volumes;
|
|
276
277
|
_network;
|
|
277
278
|
_privileged;
|
|
279
|
+
_privilegedWasSet;
|
|
280
|
+
_memory;
|
|
281
|
+
_memorySwap;
|
|
282
|
+
_cpuShares;
|
|
283
|
+
_cpuQuota;
|
|
284
|
+
_cpuPeriod;
|
|
285
|
+
_pidsLimit;
|
|
286
|
+
_readonlyRootfs;
|
|
287
|
+
_capDrop;
|
|
288
|
+
_capAdd;
|
|
289
|
+
_securityOpt;
|
|
290
|
+
_ulimits;
|
|
291
|
+
_tmpfs;
|
|
278
292
|
_workingDir;
|
|
279
293
|
_labels;
|
|
280
294
|
_instructionsOverride;
|
|
@@ -295,6 +309,19 @@ var DockerSandbox = class extends workspace.MastraSandbox {
|
|
|
295
309
|
this._volumes = options.volumes ?? {};
|
|
296
310
|
this._network = options.network;
|
|
297
311
|
this._privileged = options.privileged ?? false;
|
|
312
|
+
this._privilegedWasSet = options.privileged !== void 0;
|
|
313
|
+
this._memory = options.memory;
|
|
314
|
+
this._memorySwap = options.memorySwap;
|
|
315
|
+
this._cpuShares = options.cpuShares;
|
|
316
|
+
this._cpuQuota = options.cpuQuota;
|
|
317
|
+
this._cpuPeriod = options.cpuPeriod;
|
|
318
|
+
this._pidsLimit = options.pidsLimit;
|
|
319
|
+
this._readonlyRootfs = options.readonlyRootfs;
|
|
320
|
+
this._capDrop = options.capDrop;
|
|
321
|
+
this._capAdd = options.capAdd;
|
|
322
|
+
this._securityOpt = options.securityOpt;
|
|
323
|
+
this._ulimits = options.ulimits;
|
|
324
|
+
this._tmpfs = options.tmpfs;
|
|
298
325
|
this._workingDir = options.workingDir ?? "/workspace";
|
|
299
326
|
this._labels = {
|
|
300
327
|
...options.labels,
|
|
@@ -324,6 +351,8 @@ var DockerSandbox = class extends workspace.MastraSandbox {
|
|
|
324
351
|
this.logger.debug(`${LOG_PREFIX} Found existing container ${existing.Id}`);
|
|
325
352
|
this._container = this._docker.getContainer(existing.Id);
|
|
326
353
|
const info = await this._container.inspect();
|
|
354
|
+
this._warnOnPrivilegedHardeningConflict(info.HostConfig?.Privileged ?? this._privileged);
|
|
355
|
+
this._warnOnReconnectedHostConfigMismatch(existing.Id, info.HostConfig);
|
|
327
356
|
const actualState = info.State?.Running ? "running" : "stopped";
|
|
328
357
|
if (actualState !== "running") {
|
|
329
358
|
this.logger.debug(`${LOG_PREFIX} Container exists but not running (${actualState}), starting...`);
|
|
@@ -333,6 +362,7 @@ var DockerSandbox = class extends workspace.MastraSandbox {
|
|
|
333
362
|
this.logger.debug(`${LOG_PREFIX} Reconnected to container ${existing.Id}`);
|
|
334
363
|
return;
|
|
335
364
|
}
|
|
365
|
+
this._warnOnPrivilegedHardeningConflict(this._privileged);
|
|
336
366
|
await this._ensureImage();
|
|
337
367
|
const envArray = Object.entries(this._env).map(([k, v]) => `${k}=${v}`);
|
|
338
368
|
const binds = Object.entries(this._volumes).map(([host, container]) => `${host}:${container}`);
|
|
@@ -346,7 +376,19 @@ var DockerSandbox = class extends workspace.MastraSandbox {
|
|
|
346
376
|
HostConfig: {
|
|
347
377
|
Binds: binds.length > 0 ? binds : void 0,
|
|
348
378
|
NetworkMode: this._network,
|
|
349
|
-
Privileged: this._privileged
|
|
379
|
+
Privileged: this._privileged,
|
|
380
|
+
Memory: this._memory,
|
|
381
|
+
MemorySwap: this._memorySwap,
|
|
382
|
+
CpuShares: this._cpuShares,
|
|
383
|
+
CpuQuota: this._cpuQuota,
|
|
384
|
+
CpuPeriod: this._cpuPeriod,
|
|
385
|
+
PidsLimit: this._pidsLimit,
|
|
386
|
+
ReadonlyRootfs: this._readonlyRootfs,
|
|
387
|
+
CapDrop: this._capDrop,
|
|
388
|
+
CapAdd: this._capAdd,
|
|
389
|
+
SecurityOpt: this._securityOpt,
|
|
390
|
+
Ulimits: this._ulimits?.map(toDockerUlimit),
|
|
391
|
+
Tmpfs: this._tmpfs
|
|
350
392
|
},
|
|
351
393
|
// Keep stdin open for interactive use
|
|
352
394
|
OpenStdin: true,
|
|
@@ -356,6 +398,64 @@ var DockerSandbox = class extends workspace.MastraSandbox {
|
|
|
356
398
|
this.processes.setContainer(this._container);
|
|
357
399
|
this.logger.debug(`${LOG_PREFIX} Container started: ${this._container.id}`);
|
|
358
400
|
}
|
|
401
|
+
_warnOnPrivilegedHardeningConflict(effectivePrivileged) {
|
|
402
|
+
if (!effectivePrivileged) return;
|
|
403
|
+
const conflictedHostConfigFields = [
|
|
404
|
+
this._capDrop && this._capDrop.length > 0 ? "CapDrop" : void 0,
|
|
405
|
+
this._capAdd && this._capAdd.length > 0 ? "CapAdd" : void 0,
|
|
406
|
+
this._securityOpt && this._securityOpt.length > 0 ? "SecurityOpt" : void 0
|
|
407
|
+
].filter((field) => field !== void 0);
|
|
408
|
+
if (conflictedHostConfigFields.length === 0) return;
|
|
409
|
+
const optionNames = conflictedHostConfigFields.map(toDockerSandboxOptionName);
|
|
410
|
+
this.logger.warn(
|
|
411
|
+
`${LOG_PREFIX} Privileged containers can bypass some requested hardening controls: ${optionNames.join(", ")}`,
|
|
412
|
+
{ fields: optionNames, hostConfigFields: conflictedHostConfigFields }
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
_warnOnReconnectedHostConfigMismatch(containerId, hostConfig) {
|
|
416
|
+
if (!hostConfig) return;
|
|
417
|
+
const mismatchedHostConfigFields = this._requestedHardeningHostConfigEntries(hostConfig).filter(([field, requestedValue]) => !isHostConfigValueEqual(field, hostConfig[field], requestedValue)).map(([field]) => field);
|
|
418
|
+
if (mismatchedHostConfigFields.length === 0) return;
|
|
419
|
+
if (!this._privilegedWasSet && hostConfig.Privileged === true && mismatchedHostConfigFields.includes("Privileged")) {
|
|
420
|
+
this.logger.warn(
|
|
421
|
+
`${LOG_PREFIX} Reconnected to existing container ${containerId}; the existing container is privileged, but this DockerSandbox did not request privileged mode. Destroy and recreate the sandbox to apply the default non-privileged mode.`,
|
|
422
|
+
{ containerId, fields: ["privileged"], hostConfigFields: ["Privileged"] }
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
const remainingMismatchedHostConfigFields = mismatchedHostConfigFields.filter(
|
|
426
|
+
(field) => field !== "Privileged" || this._privilegedWasSet
|
|
427
|
+
);
|
|
428
|
+
if (remainingMismatchedHostConfigFields.length === 0) return;
|
|
429
|
+
const mismatchedOptions = remainingMismatchedHostConfigFields.map(toDockerSandboxOptionName);
|
|
430
|
+
this.logger.warn(
|
|
431
|
+
`${LOG_PREFIX} Reconnected to existing container ${containerId}; requested Docker option(s) ${mismatchedOptions.join(
|
|
432
|
+
", "
|
|
433
|
+
)} differ from inspected HostConfig field(s) ${remainingMismatchedHostConfigFields.join(
|
|
434
|
+
", "
|
|
435
|
+
)} and cannot be applied to the existing container. Destroy and recreate the sandbox to apply them.`,
|
|
436
|
+
{ containerId, fields: mismatchedOptions, hostConfigFields: remainingMismatchedHostConfigFields }
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
_requestedHardeningHostConfigEntries(hostConfig) {
|
|
440
|
+
const entries = [
|
|
441
|
+
["Memory", this._memory],
|
|
442
|
+
["MemorySwap", this._memorySwap],
|
|
443
|
+
["CpuShares", this._cpuShares],
|
|
444
|
+
["CpuQuota", this._cpuQuota],
|
|
445
|
+
["CpuPeriod", this._cpuPeriod],
|
|
446
|
+
["PidsLimit", this._pidsLimit],
|
|
447
|
+
["ReadonlyRootfs", this._readonlyRootfs],
|
|
448
|
+
["CapDrop", this._capDrop],
|
|
449
|
+
["CapAdd", this._capAdd],
|
|
450
|
+
["SecurityOpt", this._securityOpt],
|
|
451
|
+
["Ulimits", this._ulimits],
|
|
452
|
+
["Tmpfs", this._tmpfs]
|
|
453
|
+
];
|
|
454
|
+
if (this._privilegedWasSet || hostConfig?.Privileged === true) {
|
|
455
|
+
entries.unshift(["Privileged", this._privileged]);
|
|
456
|
+
}
|
|
457
|
+
return entries.filter((entry) => isPresentHostConfigValue(entry[1]));
|
|
458
|
+
}
|
|
359
459
|
async stop() {
|
|
360
460
|
const container = await this._resolveContainer();
|
|
361
461
|
if (!container) return;
|
|
@@ -517,6 +617,92 @@ function isImageNotFoundError(error) {
|
|
|
517
617
|
}
|
|
518
618
|
return false;
|
|
519
619
|
}
|
|
620
|
+
function isHostConfigValueEqual(field, actual, expected) {
|
|
621
|
+
return util.isDeepStrictEqual(normalizeHostConfigValue(field, actual), normalizeHostConfigValue(field, expected));
|
|
622
|
+
}
|
|
623
|
+
function normalizeHostConfigValue(field, value) {
|
|
624
|
+
if (value == null) return void 0;
|
|
625
|
+
if ((field === "CapAdd" || field === "CapDrop") && Array.isArray(value)) {
|
|
626
|
+
if (value.length === 0) return void 0;
|
|
627
|
+
return value.map(normalizeCapability).sort();
|
|
628
|
+
}
|
|
629
|
+
if (field === "SecurityOpt" && Array.isArray(value)) {
|
|
630
|
+
if (value.length === 0) return void 0;
|
|
631
|
+
return value.map(normalizeSecurityOpt).sort();
|
|
632
|
+
}
|
|
633
|
+
if (field === "Tmpfs" && value && typeof value === "object" && !Array.isArray(value)) {
|
|
634
|
+
if (Object.keys(value).length === 0) return void 0;
|
|
635
|
+
return Object.fromEntries(
|
|
636
|
+
Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([path, options]) => [path, typeof options === "string" ? normalizeTmpfsOptions(options) : options])
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
if (Array.isArray(value)) {
|
|
640
|
+
if (field === "Ulimits" && value.length === 0) return void 0;
|
|
641
|
+
return value.map((nestedValue) => normalizeHostConfigValue(field, nestedValue)).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
642
|
+
}
|
|
643
|
+
if (field === "Ulimits" && value && typeof value === "object") {
|
|
644
|
+
return normalizeUlimit(value);
|
|
645
|
+
}
|
|
646
|
+
if (value && typeof value === "object") {
|
|
647
|
+
return Object.fromEntries(
|
|
648
|
+
Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, nestedValue]) => [key, normalizeHostConfigValue(field, nestedValue)])
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
return value;
|
|
652
|
+
}
|
|
653
|
+
function isPresentHostConfigValue(value) {
|
|
654
|
+
if (value == null) return false;
|
|
655
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
656
|
+
if (value && typeof value === "object") return Object.keys(value).length > 0;
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
function normalizeCapability(capability) {
|
|
660
|
+
return typeof capability === "string" ? capability.toUpperCase().replace(/^CAP_/, "") : capability;
|
|
661
|
+
}
|
|
662
|
+
function normalizeTmpfsOptions(options) {
|
|
663
|
+
return options.split(",").map((option) => option.trim()).filter(Boolean).sort().join(",");
|
|
664
|
+
}
|
|
665
|
+
function normalizeSecurityOpt(option) {
|
|
666
|
+
if (typeof option !== "string") return option;
|
|
667
|
+
const noNewPrivileges = option.match(/^no-new-privileges[:=](.+)$/i);
|
|
668
|
+
if (noNewPrivileges) {
|
|
669
|
+
return `no-new-privileges=${noNewPrivileges[1]}`;
|
|
670
|
+
}
|
|
671
|
+
return option;
|
|
672
|
+
}
|
|
673
|
+
function normalizeUlimit(value) {
|
|
674
|
+
const record = value;
|
|
675
|
+
return {
|
|
676
|
+
name: record.name ?? record.Name,
|
|
677
|
+
soft: record.soft ?? record.Soft,
|
|
678
|
+
hard: record.hard ?? record.Hard
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
function toDockerUlimit(ulimit) {
|
|
682
|
+
return {
|
|
683
|
+
Name: ulimit.name,
|
|
684
|
+
Soft: ulimit.soft,
|
|
685
|
+
Hard: ulimit.hard
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
function toDockerSandboxOptionName(field) {
|
|
689
|
+
const optionNames = {
|
|
690
|
+
Privileged: "privileged",
|
|
691
|
+
Memory: "memory",
|
|
692
|
+
MemorySwap: "memorySwap",
|
|
693
|
+
CpuShares: "cpuShares",
|
|
694
|
+
CpuQuota: "cpuQuota",
|
|
695
|
+
CpuPeriod: "cpuPeriod",
|
|
696
|
+
PidsLimit: "pidsLimit",
|
|
697
|
+
ReadonlyRootfs: "readonlyRootfs",
|
|
698
|
+
CapDrop: "capDrop",
|
|
699
|
+
CapAdd: "capAdd",
|
|
700
|
+
SecurityOpt: "securityOpt",
|
|
701
|
+
Ulimits: "ulimits",
|
|
702
|
+
Tmpfs: "tmpfs"
|
|
703
|
+
};
|
|
704
|
+
return optionNames[field] ?? String(field);
|
|
705
|
+
}
|
|
520
706
|
|
|
521
707
|
// src/provider.ts
|
|
522
708
|
var dockerSandboxProvider = {
|
|
@@ -559,6 +745,68 @@ var dockerSandboxProvider = {
|
|
|
559
745
|
type: "boolean",
|
|
560
746
|
description: "Run in privileged mode",
|
|
561
747
|
default: false
|
|
748
|
+
},
|
|
749
|
+
memory: {
|
|
750
|
+
type: "number",
|
|
751
|
+
description: "Memory limit in bytes"
|
|
752
|
+
},
|
|
753
|
+
memorySwap: {
|
|
754
|
+
type: "number",
|
|
755
|
+
description: "Total memory plus swap in bytes"
|
|
756
|
+
},
|
|
757
|
+
cpuShares: {
|
|
758
|
+
type: "number",
|
|
759
|
+
description: "CPU shares relative weight"
|
|
760
|
+
},
|
|
761
|
+
cpuQuota: {
|
|
762
|
+
type: "number",
|
|
763
|
+
description: "CPU quota in microseconds per period"
|
|
764
|
+
},
|
|
765
|
+
cpuPeriod: {
|
|
766
|
+
type: "number",
|
|
767
|
+
description: "CPU period in microseconds"
|
|
768
|
+
},
|
|
769
|
+
pidsLimit: {
|
|
770
|
+
type: "number",
|
|
771
|
+
description: "Maximum number of PIDs in the container"
|
|
772
|
+
},
|
|
773
|
+
readonlyRootfs: {
|
|
774
|
+
type: "boolean",
|
|
775
|
+
description: "Mount the container root filesystem as read-only"
|
|
776
|
+
},
|
|
777
|
+
capDrop: {
|
|
778
|
+
type: "array",
|
|
779
|
+
description: "Linux capabilities to drop",
|
|
780
|
+
items: { type: "string" }
|
|
781
|
+
},
|
|
782
|
+
capAdd: {
|
|
783
|
+
type: "array",
|
|
784
|
+
description: "Linux capabilities to add",
|
|
785
|
+
items: { type: "string" }
|
|
786
|
+
},
|
|
787
|
+
securityOpt: {
|
|
788
|
+
type: "array",
|
|
789
|
+
description: "Docker security options",
|
|
790
|
+
items: { type: "string" }
|
|
791
|
+
},
|
|
792
|
+
ulimits: {
|
|
793
|
+
type: "array",
|
|
794
|
+
description: "Ulimit entries for Docker HostConfig.Ulimits",
|
|
795
|
+
items: {
|
|
796
|
+
type: "object",
|
|
797
|
+
required: ["name", "soft", "hard"],
|
|
798
|
+
additionalProperties: false,
|
|
799
|
+
properties: {
|
|
800
|
+
name: { type: "string" },
|
|
801
|
+
soft: { type: "number" },
|
|
802
|
+
hard: { type: "number" }
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
tmpfs: {
|
|
807
|
+
type: "object",
|
|
808
|
+
description: "tmpfs mount paths with options",
|
|
809
|
+
additionalProperties: { type: "string" }
|
|
562
810
|
}
|
|
563
811
|
}
|
|
564
812
|
},
|