Skip to content

Commit 375a26b

Browse files
committed
Please add CPU and memory limits
Why is everything i64
1 parent 145544f commit 375a26b

File tree

3 files changed

+358
-2
lines changed

3 files changed

+358
-2
lines changed

testcontainers/src/core/containers/request.rs

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,64 @@ pub struct ContainerRequest<I: Image> {
4949
pub(crate) startup_timeout: Option<Duration>,
5050
pub(crate) working_dir: Option<String>,
5151
pub(crate) log_consumers: Vec<Box<dyn LogConsumer + 'static>>,
52+
53+
/// The length of a CPU period in microseconds. Default is 100000, this configures how
54+
/// CFS will schedule the threads for this container. Normally you don't adjust this and
55+
/// just set the CPU quota or nano CPUs. You might want to set this if you want to increase
56+
/// or reduce context-switching the container is subjected to.
57+
pub(crate) cpu_period: Option<i64>,
58+
59+
/// Microseconds of CPU time that the container can get in a CPU period.
60+
/// Most users will want to set CPU quota to their desired CPU count * 100000.
61+
/// For example, to limit a container to 2 CPUs, set CPU quota to 200000.
62+
/// This is based on the default CPU period of 100000.
63+
/// If CPU quota is set to 0, the container will not be limited.
64+
pub(crate) cpu_quota: Option<i64>,
65+
66+
/// The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks.
67+
pub(crate) cpu_realtime_period: Option<i64>,
68+
69+
/// The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks.
70+
pub(crate) cpu_realtime_runtime: Option<i64>,
71+
72+
/// CPUs in which to allow execution (e.g., `0-3`, `0,1`).
73+
/// Core pinning should help with performance consistency and context switching in some cases.
74+
pub(crate) cpuset_cpus: Option<String>,
75+
76+
/// CPU quota in units of 10<sup>-9</sup> CPUs. This is basically what the --cpus flag turns into, but the
77+
/// raw value is denominated in billionths of a CPU. cpu_period and cpu_quota give you more control over the scheduler.
78+
pub nano_cpus: Option<i64>,
79+
80+
/// Memory limit for the container, the _minimum_ is 6 MiB.
81+
/// This is the same as `HostConfig::memory`.
82+
pub(crate) memory: Option<i64>,
83+
84+
/// Memory reservation, soft limit. Analogous to the JVM's `-Xms` option.
85+
/// The _minimum_ is 6 MiB.
86+
/// This is the same as `HostConfig::memory_reservation`.
87+
pub(crate) memory_reservation: Option<i64>,
88+
89+
/// Total memory limit (memory + swap). Set as `-1` to enable unlimited swap.
90+
/// Same 6 MiB minimum as `memory`.
91+
pub memory_swap: Option<i64>,
92+
93+
/// Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
94+
pub memory_swappiness: Option<i64>,
95+
96+
/// Disable OOM Killer for the container. This will not do anything unless -m (memory limit, cf. memory on this struct) is set.
97+
/// You can disable OOM-killer by writing "1" to memory.oom_control file, as:
98+
/// ```ignore
99+
/// echo 1 > memory.oom_control
100+
/// ```
101+
/// This operation is only allowed to the top cgroup of sub-hierarchy.
102+
/// If OOM-killer is disabled, tasks under cgroup will hang/sleep
103+
/// in memory cgroup's OOM-waitqueue when they request accountable memory.
104+
/// https://lwn.net/Articles/432224/
105+
pub oom_kill_disable: Option<bool>,
106+
107+
/// Tune a container's PIDs limit. Set `0` or `-1` for unlimited, or `null` to not change.
108+
pub pids_limit: Option<i64>,
109+
52110
#[cfg(feature = "reusable-containers")]
53111
pub(crate) reuse: crate::ReuseDirective,
54112
pub(crate) user: Option<String>,
@@ -211,6 +269,42 @@ impl<I: Image> ContainerRequest<I> {
211269
self.working_dir.as_deref()
212270
}
213271

272+
pub fn cpu_period(&self) -> Option<i64> {
273+
self.cpu_period
274+
}
275+
pub fn cpu_quota(&self) -> Option<i64> {
276+
self.cpu_quota
277+
}
278+
pub fn cpu_realtime_period(&self) -> Option<i64> {
279+
self.cpu_realtime_period
280+
}
281+
pub fn cpu_realtime_runtime(&self) -> Option<i64> {
282+
self.cpu_realtime_runtime
283+
}
284+
pub fn cpuset_cpus(&self) -> Option<&str> {
285+
self.cpuset_cpus.as_deref()
286+
}
287+
pub fn nano_cpus(&self) -> Option<i64> {
288+
self.nano_cpus
289+
}
290+
pub fn memory(&self) -> Option<i64> {
291+
self.memory
292+
}
293+
pub fn memory_reservation(&self) -> Option<i64> {
294+
self.memory_reservation
295+
}
296+
pub fn memory_swap(&self) -> Option<i64> {
297+
self.memory_swap
298+
}
299+
pub fn memory_swappiness(&self) -> Option<i64> {
300+
self.memory_swappiness
301+
}
302+
pub fn oom_kill_disable(&self) -> Option<bool> {
303+
self.oom_kill_disable
304+
}
305+
pub fn pids_limit(&self) -> Option<i64> {
306+
self.pids_limit
307+
}
214308
/// Indicates that the container will not be stopped when it is dropped
215309
#[cfg(feature = "reusable-containers")]
216310
pub fn reuse(&self) -> crate::ReuseDirective {
@@ -276,6 +370,18 @@ impl<I: Image> From<I> for ContainerRequest<I> {
276370
startup_timeout: None,
277371
working_dir: None,
278372
log_consumers: vec![],
373+
cpu_period: None,
374+
cpu_quota: None,
375+
cpu_realtime_period: None,
376+
cpu_realtime_runtime: None,
377+
cpuset_cpus: None,
378+
nano_cpus: None,
379+
memory: None,
380+
memory_reservation: None,
381+
memory_swap: None,
382+
memory_swappiness: None,
383+
oom_kill_disable: None,
384+
pids_limit: None,
279385
#[cfg(feature = "reusable-containers")]
280386
reuse: crate::ReuseDirective::Never,
281387
user: None,
@@ -336,8 +442,12 @@ impl<I: Image + Debug> Debug for ContainerRequest<I> {
336442
.field("working_dir", &self.working_dir)
337443
.field("user", &self.user)
338444
.field("ready_conditions", &self.ready_conditions)
339-
.field("health_check", &self.health_check);
340-
445+
.field("health_check", &self.health_check)
446+
.field("cpu_period", &self.cpu_period)
447+
.field("cpu_quota", &self.cpu_quota)
448+
.field("cpu_realtime_period", &self.cpu_realtime_period)
449+
.field("cpu_realtime_runtime", &self.cpu_realtime_runtime)
450+
.field("cpuset_cpus", &self.cpuset_cpus);
341451
#[cfg(feature = "reusable-containers")]
342452
repr.field("reusable", &self.reuse);
343453

testcontainers/src/core/image/image_ext.rs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,66 @@ pub trait ImageExt<I: Image> {
210210
/// Allows to follow the container logs for the whole lifecycle of the container, starting from the creation.
211211
fn with_log_consumer(self, log_consumer: impl LogConsumer + 'static) -> ContainerRequest<I>;
212212

213+
/// Sets the CPU period for the container.
214+
/// The default is defined by the underlying image.
215+
/// The length of a CPU period in microseconds.
216+
/// https://docs.docker.com/engine/reference/commandline/run/#cpu-period
217+
fn with_cpu_period(self, cpu_period: impl Into<i64>) -> ContainerRequest<I>;
218+
219+
/// Sets the CPU quota for the container.
220+
/// The default is defined by the underlying image.
221+
/// Microseconds of CPU time that the container can get in a CPU period.
222+
/// https://docs.docker.com/engine/reference/commandline/run/#cpu-quota
223+
/// Most users will want to set CPU quota to their desired CPU count * 100000.
224+
/// For example, to limit a container to 2 CPUs, set CPU quota to 200000.
225+
/// This is based on the default CPU period of 100000.
226+
/// If CPU quota is set to 0, the container will not be limited.
227+
fn with_cpu_quota(self, cpu_quota: impl Into<i64>) -> ContainerRequest<I>;
228+
229+
/// Sets the CPU realtime period for the container.
230+
/// The default is defined by the underlying image.
231+
/// The length of a CPU real-time period in microseconds.
232+
fn with_cpu_realtime_period(self, cpu_realtime_period: impl Into<i64>) -> ContainerRequest<I>;
233+
234+
/// Sets the CPU realtime runtime for the container.
235+
fn with_cpu_realtime_runtime(self, cpu_realtime_runtime: impl Into<i64>)
236+
-> ContainerRequest<I>;
237+
238+
/// Sets the CPUs in which to allow execution (e.g., `0-3`, `0,1`).
239+
/// Core pinning should help with performance consistency and context switching in some cases.
240+
/// The default is defined by the underlying image.
241+
fn with_cpuset_cpus(self, cpuset_cpus: impl Into<String>) -> ContainerRequest<I>;
242+
243+
/// Memory limit for the container, the _minimum_ is 6 MiB.
244+
/// This is the same as `HostConfig::memory`.
245+
fn with_memory(self, bytes: i64) -> ContainerRequest<I>;
246+
247+
/// Memory reservation, soft limit. Analogous to the JVM's `-Xms` option.
248+
/// The _minimum_ is 6 MiB.
249+
/// This is the same as `HostConfig::memory_reservation`.
250+
fn with_memory_reservation(self, bytes: i64) -> ContainerRequest<I>;
251+
252+
/// Total memory limit (memory + swap). Set as `-1` to enable unlimited swap.
253+
/// Same 6 MiB minimum as `memory`. I do not know why everything is i64.
254+
fn with_memory_swap(self, bytes: i64) -> ContainerRequest<I>;
255+
256+
/// Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
257+
fn with_memory_swappiness(self, swappiness: i64) -> ContainerRequest<I>;
258+
259+
/// Disable OOM Killer for the container. This will not do anything unless -m (memory limit, cf. memory on this struct) is set.
260+
/// You can disable OOM-killer by writing "1" to memory.oom_control file, as:
261+
/// ```ignore
262+
/// echo 1 > memory.oom_control
263+
/// ```
264+
/// This operation is only allowed to the top cgroup of sub-hierarchy.
265+
/// If OOM-killer is disabled, tasks under cgroup will hang/sleep
266+
/// in memory cgroup's OOM-waitqueue when they request accountable memory.
267+
/// https://lwn.net/Articles/432224/
268+
fn with_oom_kill_disable(self, disable: bool) -> ContainerRequest<I>;
269+
270+
/// Tune a container's PIDs limit. Set `0` or `-1` for unlimited, or `null` to not change.
271+
fn with_pids_limit(self, limit: i64) -> ContainerRequest<I>;
272+
213273
/// Flag the container as being exempt from the default `testcontainers` remove-on-drop lifecycle,
214274
/// indicating that the container should be kept running, and that executions with the same configuration
215275
/// reuse it instead of starting a "fresh" container instance.
@@ -526,6 +586,97 @@ impl<RI: Into<ContainerRequest<I>>, I: Image> ImageExt<I> for RI {
526586
container_req
527587
}
528588

589+
fn with_cpu_period(self, cpu_period: impl Into<i64>) -> ContainerRequest<I> {
590+
let container_req = self.into();
591+
ContainerRequest {
592+
cpu_period: Some(cpu_period.into()),
593+
..container_req
594+
}
595+
}
596+
597+
fn with_cpu_quota(self, cpu_quota: impl Into<i64>) -> ContainerRequest<I> {
598+
let container_req = self.into();
599+
ContainerRequest {
600+
cpu_quota: Some(cpu_quota.into()),
601+
..container_req
602+
}
603+
}
604+
605+
fn with_cpu_realtime_period(self, cpu_realtime_period: impl Into<i64>) -> ContainerRequest<I> {
606+
let container_req = self.into();
607+
ContainerRequest {
608+
cpu_realtime_period: Some(cpu_realtime_period.into()),
609+
..container_req
610+
}
611+
}
612+
613+
fn with_cpu_realtime_runtime(
614+
self,
615+
cpu_realtime_runtime: impl Into<i64>,
616+
) -> ContainerRequest<I> {
617+
let container_req = self.into();
618+
ContainerRequest {
619+
cpu_realtime_runtime: Some(cpu_realtime_runtime.into()),
620+
..container_req
621+
}
622+
}
623+
624+
fn with_cpuset_cpus(self, cpuset_cpus: impl Into<String>) -> ContainerRequest<I> {
625+
let container_req = self.into();
626+
ContainerRequest {
627+
cpuset_cpus: Some(cpuset_cpus.into()),
628+
..container_req
629+
}
630+
}
631+
632+
fn with_memory(self, bytes: i64) -> ContainerRequest<I> {
633+
let container_req = self.into();
634+
ContainerRequest {
635+
memory: Some(bytes),
636+
..container_req
637+
}
638+
}
639+
640+
fn with_memory_reservation(self, bytes: i64) -> ContainerRequest<I> {
641+
let container_req = self.into();
642+
ContainerRequest {
643+
memory_reservation: Some(bytes),
644+
..container_req
645+
}
646+
}
647+
648+
fn with_memory_swap(self, bytes: i64) -> ContainerRequest<I> {
649+
let container_req = self.into();
650+
ContainerRequest {
651+
memory_swap: Some(bytes),
652+
..container_req
653+
}
654+
}
655+
656+
fn with_memory_swappiness(self, swappiness: i64) -> ContainerRequest<I> {
657+
let container_req = self.into();
658+
ContainerRequest {
659+
memory_swappiness: Some(swappiness),
660+
..container_req
661+
}
662+
}
663+
664+
fn with_oom_kill_disable(self, disable: bool) -> ContainerRequest<I> {
665+
let container_req = self.into();
666+
ContainerRequest {
667+
oom_kill_disable: Some(disable),
668+
..container_req
669+
}
670+
}
671+
672+
fn with_pids_limit(self, limit: i64) -> ContainerRequest<I> {
673+
let container_req = self.into();
674+
ContainerRequest {
675+
pids_limit: Some(limit),
676+
..container_req
677+
}
678+
}
679+
529680
#[cfg(feature = "reusable-containers")]
530681
fn with_reuse(self, reuse: ReuseDirective) -> ContainerRequest<I> {
531682
ContainerRequest {

0 commit comments

Comments
 (0)