1pub fn hostname() -> String {
18 hostname::get()
19 .unwrap_or_default()
20 .into_string()
21 .unwrap_or_default()
22}
23
24#[derive(Debug, Clone, Copy)]
25pub enum CgroupVersion {
26 V1,
27 V2,
28}
29
30pub enum Controller {
32 Cpu,
33 Memory,
34}
35
36const DEFAULT_CGROUP_ROOT_HIERARCYHY: &str = "/sys/fs/cgroup";
38const DEFAULT_CGROUP_V2_CONTROLLER_LIST_PATH: &str = "/sys/fs/cgroup/cgroup.controllers";
39const DEFAULT_CGROUP_MAX_INDICATOR: &str = "max";
40
41mod runtime {
42 use std::env;
43 use std::path::Path;
44
45 use thiserror_ext::AsReport;
46
47 use super::CgroupVersion;
48 use super::util::parse_controller_enable_file_for_cgroup_v2;
49 const DEFAULT_DOCKER_ENV_PATH: &str = "/.dockerenv";
50 const DEFAULT_LINUX_IDENTIFIER: &str = "linux";
51 const DEFAULT_IN_CONTAINER_ENV_VARIABLE: &str = "IN_CONTAINER";
52 const DEFAULT_KUBERNETES_SECRETS_PATH: &str = "/var/run/secrets/kubernetes.io";
53
54 fn is_linux_machine() -> bool {
55 env::consts::OS.eq(DEFAULT_LINUX_IDENTIFIER)
56 }
57
58 fn is_running_in_container() -> bool {
61 return env_var_check_if_running_in_container()
62 || docker_env_exists()
63 || is_running_in_kubernetes_pod();
64
65 fn docker_env_exists() -> bool {
67 Path::new(DEFAULT_DOCKER_ENV_PATH).exists()
68 }
69
70 fn env_var_check_if_running_in_container() -> bool {
72 env::var(DEFAULT_IN_CONTAINER_ENV_VARIABLE).is_ok()
73 }
74
75 fn is_running_in_kubernetes_pod() -> bool {
77 Path::new(DEFAULT_KUBERNETES_SECRETS_PATH).exists()
78 }
79 }
80
81 pub fn is_controller_activated(
86 controller_type: super::Controller,
87 cgroup_version: CgroupVersion,
88 ) -> bool {
89 let controller_name: &str = match controller_type {
90 super::Controller::Cpu => "cpu",
91 super::Controller::Memory => "memory",
92 };
93 match cgroup_version {
94 super::CgroupVersion::V1 => Path::new(super::DEFAULT_CGROUP_ROOT_HIERARCYHY)
95 .join(controller_name)
96 .is_dir(),
97 super::CgroupVersion::V2 => parse_controller_enable_file_for_cgroup_v2(
98 super::DEFAULT_CGROUP_V2_CONTROLLER_LIST_PATH,
99 controller_name,
100 ),
101 }
102 }
103
104 fn cgroup_exists() -> bool {
106 Path::new(super::DEFAULT_CGROUP_ROOT_HIERARCYHY).is_dir()
107 }
108
109 pub fn get_resource<T>(
110 desc: &str,
111 controller_type: super::Controller,
112 get_system: fn() -> T,
113 get_container: fn(CgroupVersion) -> Result<T, std::io::Error>,
114 ) -> T {
115 if !is_linux_machine() || !is_running_in_container() || !cgroup_exists() {
116 return get_system();
117 };
118
119 let cgroup_version = if Path::new(super::DEFAULT_CGROUP_V2_CONTROLLER_LIST_PATH).exists() {
121 super::CgroupVersion::V2
122 } else {
123 super::CgroupVersion::V1
124 };
125 if !is_controller_activated(controller_type, cgroup_version) {
126 return get_system();
127 }
128
129 match get_container(cgroup_version) {
130 Ok(value) => value,
131 Err(err) => {
132 tracing::warn!(
133 error = %err.as_report(),
134 cgroup_version = ?cgroup_version,
135 "failed to get {desc} in container, use system value instead"
136 );
137 get_system()
138 }
139 }
140 }
141}
142
143pub mod memory {
144 use sysinfo::System;
145
146 use super::runtime::get_resource;
147
148 const V1_MEMORY_LIMIT_PATH: &str = "/sys/fs/cgroup/memory/memory.limit_in_bytes";
150 const V1_MEMORY_CURRENT_PATH: &str = "/sys/fs/cgroup/memory/memory.usage_in_bytes";
151 const V2_MEMORY_LIMIT_PATH: &str = "/sys/fs/cgroup/memory.max";
152 const V2_MEMORY_CURRENT_PATH: &str = "/sys/fs/cgroup/memory.current";
153
154 pub fn get_system_memory() -> usize {
156 let mut sys = System::new();
157 sys.refresh_memory();
158 sys.total_memory() as usize
159 }
160
161 pub fn get_system_memory_used() -> usize {
163 let mut sys = System::new();
164 sys.refresh_memory();
165 sys.used_memory() as usize
166 }
167
168 pub fn total_memory_used_bytes() -> usize {
182 get_resource(
183 "memory used",
184 super::Controller::Memory,
185 get_system_memory_used,
186 get_container_memory_used,
187 )
188 }
189
190 pub fn system_memory_available_bytes() -> usize {
204 get_resource(
205 "memory available",
206 super::Controller::Memory,
207 get_system_memory,
208 get_container_memory_limit,
209 )
210 }
211
212 fn get_container_memory_limit(
217 cgroup_version: super::CgroupVersion,
218 ) -> Result<usize, std::io::Error> {
219 let limit_path = match cgroup_version {
220 super::CgroupVersion::V1 => V1_MEMORY_LIMIT_PATH,
221 super::CgroupVersion::V2 => V2_MEMORY_LIMIT_PATH,
222 };
223 let system = get_system_memory();
224 let value = super::util::read_usize_or_max(limit_path, system)?;
225 Ok(std::cmp::min(value, system))
226 }
227
228 fn get_container_memory_used(
231 cgroup_version: super::CgroupVersion,
232 ) -> Result<usize, std::io::Error> {
233 let usage_path = match cgroup_version {
234 super::CgroupVersion::V1 => V1_MEMORY_CURRENT_PATH,
235 super::CgroupVersion::V2 => V2_MEMORY_CURRENT_PATH,
236 };
237 let system = get_system_memory_used();
238 let value = super::util::read_usize_or_max(usage_path, system)?;
239 Ok(std::cmp::min(value, system))
240 }
241}
242
243pub mod cpu {
244 use std::thread;
245
246 use thiserror_ext::AsReport;
247
248 use super::runtime::get_resource;
249 use super::util::parse_error;
250
251 const V1_CPU_QUOTA_PATH: &str = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us";
253 const V1_CPU_PERIOD_PATH: &str = "/sys/fs/cgroup/cpu/cpu.cfs_period_us";
254 const V2_CPU_LIMIT_PATH: &str = "/sys/fs/cgroup/cpu.max";
255
256 pub fn total_cpu_available() -> f32 {
272 get_resource(
273 "cpu quota",
274 super::Controller::Cpu,
275 get_system_cpu,
276 get_container_cpu_limit,
277 )
278 }
279
280 fn get_container_cpu_limit(
282 cgroup_version: super::CgroupVersion,
283 ) -> Result<f32, std::io::Error> {
284 let max_cpu = get_system_cpu();
285 match cgroup_version {
286 super::CgroupVersion::V1 => {
287 get_cpu_limit_v1(V1_CPU_QUOTA_PATH, V1_CPU_PERIOD_PATH, max_cpu)
288 }
289 super::CgroupVersion::V2 => get_cpu_limit_v2(V2_CPU_LIMIT_PATH, max_cpu),
290 }
291 }
292
293 pub fn get_system_cpu() -> f32 {
295 match thread::available_parallelism() {
296 Ok(available_parallelism) => available_parallelism.get() as f32,
297 Err(e) => panic!(
298 "Failed to get available parallelism, error: {}",
299 e.as_report()
300 ),
301 }
302 }
303
304 pub fn get_cpu_limit_v1(
306 quota_path: &str,
307 period_path: &str,
308 max_value: f32,
309 ) -> Result<f32, std::io::Error> {
310 let content = std::fs::read_to_string(quota_path)?;
311 let cpu_quota = content
312 .trim()
313 .parse::<i64>()
314 .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "not a number"))?;
315 if cpu_quota < 0 {
318 return Ok(max_value);
319 }
320
321 let cpu_period = super::util::read_usize(period_path)?;
322
323 Ok((cpu_quota as f32) / (cpu_period as f32))
324 }
325
326 pub fn get_cpu_limit_v2(limit_path: &str, max_value: f32) -> Result<f32, std::io::Error> {
328 let cpu_limit_string = fs_err::read_to_string(limit_path)?;
329
330 let cpu_data: Vec<&str> = cpu_limit_string.split_whitespace().collect();
331 match cpu_data.get(0..2) {
332 Some(cpu_data_values) => {
333 if cpu_data_values[0] == super::DEFAULT_CGROUP_MAX_INDICATOR {
334 return Ok(max_value);
335 }
336 let cpu_quota = cpu_data_values[0]
337 .parse::<usize>()
338 .map_err(|e| parse_error(limit_path, &cpu_limit_string, e))?;
339 let cpu_period = cpu_data_values[1]
340 .parse::<usize>()
341 .map_err(|e| parse_error(limit_path, &cpu_limit_string, e))?;
342 Ok((cpu_quota as f32) / (cpu_period as f32))
343 }
344 None => Err(std::io::Error::new(
345 std::io::ErrorKind::InvalidData,
346 format!(
347 "Invalid format in Cgroup CPU interface file, path: {limit_path}, content: {cpu_limit_string}"
348 ),
349 )),
350 }
351 }
352}
353
354mod util {
355 pub fn parse_controller_enable_file_for_cgroup_v2(
357 file_path: &str,
358 controller_name: &str,
359 ) -> bool {
360 match fs_err::read_to_string(file_path) {
361 Ok(controller_string) => {
362 for controller in controller_string.split_whitespace() {
363 if controller.eq(controller_name) {
364 return true;
365 };
366 }
367 false
368 }
369 Err(_) => false,
370 }
371 }
372
373 pub fn parse_error(
374 file_path: &str,
375 content: &str,
376 e: impl std::fmt::Display,
377 ) -> std::io::Error {
378 std::io::Error::new(
379 std::io::ErrorKind::InvalidData,
380 format!("failed to parse, path: {file_path}, content: {content}, error: {e}"),
381 )
382 }
383
384 pub fn read_usize(file_path: &str) -> Result<usize, std::io::Error> {
386 let content = fs_err::read_to_string(file_path)?;
387 let limit_val = content
388 .trim()
389 .parse::<usize>()
390 .map_err(|e| parse_error(file_path, &content, e))?;
391 Ok(limit_val)
392 }
393
394 pub fn read_usize_or_max(file_path: &str, max_value: usize) -> Result<usize, std::io::Error> {
397 let content = fs_err::read_to_string(file_path)?;
398 if content.trim() == super::DEFAULT_CGROUP_MAX_INDICATOR {
399 return Ok(max_value);
400 }
401 let limit_val = content
402 .trim()
403 .parse::<usize>()
404 .map_err(|e| parse_error(file_path, &content, e))?;
405 Ok(limit_val)
406 }
407
408 #[cfg(test)]
409 mod tests {
410 use std::collections::HashMap;
411 use std::io::prelude::*;
412 use std::thread;
413
414 use super::*;
415 use crate::cpu::{self, get_system_cpu};
416 use crate::memory::get_system_memory;
417 use crate::{Controller, DEFAULT_CGROUP_MAX_INDICATOR};
418 const DEFAULT_NON_EXISTENT_PATH: &str = "default-non-existent-path";
419
420 #[test]
421 fn test_read_integer_from_file_path() {
422 struct TestCase {
423 file_exists: bool,
424 value_in_file: String,
425 expected: Result<usize, std::io::Error>,
426 }
427
428 let test_cases = HashMap::from([
429 (
430 "valid-integer-value-in-file",
431 TestCase {
432 file_exists: true,
433 value_in_file: String::from("10000"),
434 expected: Ok(10000),
435 },
436 ),
437 (
438 "valid-integer-value-in-file-with-spaces-after",
439 TestCase {
440 file_exists: true,
441 value_in_file: String::from("10000 "),
442 expected: Ok(10000),
443 },
444 ),
445 (
446 "valid-integer-value-in-file-with-spaces-before",
447 TestCase {
448 file_exists: true,
449 value_in_file: String::from(" 10000"),
450 expected: Ok(10000),
451 },
452 ),
453 (
454 "invalid-integer-value-in-file",
455 TestCase {
456 file_exists: true,
457 value_in_file: String::from("test-string"),
458 expected: Err(std::io::Error::new(
459 std::io::ErrorKind::InvalidData,
460 "not a number",
461 )),
462 },
463 ),
464 (
465 "file-not-exist",
466 TestCase {
467 file_exists: false,
468 value_in_file: String::from(""),
469 expected: Err(std::io::Error::new(
470 std::io::ErrorKind::NotFound,
471 "File not found",
472 )),
473 },
474 ),
475 (
476 "max-value-in-file",
477 TestCase {
478 file_exists: true,
479 value_in_file: String::from(DEFAULT_CGROUP_MAX_INDICATOR),
480 expected: Err(std::io::Error::new(
481 std::io::ErrorKind::InvalidData,
482 "not a number",
483 )),
484 },
485 ),
486 ]);
487
488 for tc in test_cases {
489 let curr_test_case = &tc.1;
490 let mut file: tempfile::NamedTempFile;
491 let mut test_file_path = String::from(DEFAULT_NON_EXISTENT_PATH);
492 if curr_test_case.file_exists {
493 file = tempfile::NamedTempFile::new()
494 .expect("Error encountered while creating file!");
495 file.as_file_mut()
496 .write_all(curr_test_case.value_in_file.as_bytes())
497 .expect("Error while writing to file");
498 test_file_path = String::from(file.path().to_str().unwrap())
499 }
500 match read_usize(&test_file_path) {
501 Ok(int_val) => assert_eq!(&int_val, curr_test_case.expected.as_ref().unwrap()),
502 Err(e) => assert_eq!(
503 e.kind(),
504 curr_test_case.expected.as_ref().unwrap_err().kind()
505 ),
506 }
507 }
508 }
509
510 #[test]
511 fn test_get_value_from_file() {
512 struct TestCase {
513 file_exists: bool,
514 value_in_file: String,
515 expected: Result<usize, std::io::Error>,
516 }
517
518 let test_cases = HashMap::from([
519 (
520 "valid-integer-value-in-file",
521 TestCase {
522 file_exists: true,
523 value_in_file: String::from("10000"),
524 expected: Ok(10000),
525 },
526 ),
527 (
528 "valid-integer-value-in-file-with-spaces-after",
529 TestCase {
530 file_exists: true,
531 value_in_file: String::from("10000 "),
532 expected: Ok(10000),
533 },
534 ),
535 (
536 "valid-integer-value-in-file-with-spaces-before",
537 TestCase {
538 file_exists: true,
539 value_in_file: String::from(" 10000"),
540 expected: Ok(10000),
541 },
542 ),
543 (
544 "invalid-integer-value-in-file",
545 TestCase {
546 file_exists: true,
547 value_in_file: String::from("test-string"),
548 expected: Err(std::io::Error::new(
549 std::io::ErrorKind::InvalidData,
550 "not a number",
551 )),
552 },
553 ),
554 (
555 "file-not-exist",
556 TestCase {
557 file_exists: false,
558 value_in_file: String::from(""),
559 expected: Err(std::io::Error::new(
560 std::io::ErrorKind::NotFound,
561 "File not found",
562 )),
563 },
564 ),
565 (
566 "max-value-in-file",
567 TestCase {
568 file_exists: true,
569 value_in_file: String::from(DEFAULT_CGROUP_MAX_INDICATOR),
570 expected: Ok(get_system_memory()),
571 },
572 ),
573 ]);
574
575 for tc in test_cases {
576 let curr_test_case = &tc.1;
577 let mut file: tempfile::NamedTempFile;
578 let mut test_file_path = String::from(DEFAULT_NON_EXISTENT_PATH);
579 if curr_test_case.file_exists {
580 file = tempfile::NamedTempFile::new()
581 .expect("Error encountered while creating file!");
582 file.as_file_mut()
583 .write_all(curr_test_case.value_in_file.as_bytes())
584 .expect("Error while writing to file");
585 test_file_path = String::from(file.path().to_str().unwrap())
586 }
587 match read_usize_or_max(&test_file_path, get_system_memory()) {
588 Ok(int_val) => assert_eq!(&int_val, curr_test_case.expected.as_ref().unwrap()),
589 Err(e) => assert_eq!(
590 e.kind(),
591 curr_test_case.expected.as_ref().unwrap_err().kind()
592 ),
593 }
594 }
595 }
596
597 #[test]
598 fn test_get_cpu_limit_v1() {
599 #[derive(Debug)]
600 struct TestCase {
601 file_exists: bool,
602 value_in_quota_file: String,
603 value_in_period_file: String,
604 expected: Result<f32, std::io::Error>,
605 }
606
607 let test_cases = HashMap::from([
608 (
609 "default-values",
610 TestCase {
611 file_exists: true,
612 value_in_quota_file: String::from("-1"),
613 value_in_period_file: String::from("10000"),
614 expected: Ok(thread::available_parallelism().unwrap().get() as f32),
615 },
616 ),
617 (
618 "valid-values-in-file",
619 TestCase {
620 file_exists: true,
621 value_in_quota_file: String::from("10000"),
622 value_in_period_file: String::from("20000"),
623 expected: Ok(10000.0 / 20000.0),
624 },
625 ),
626 (
627 "empty-value-in-files",
628 TestCase {
629 file_exists: true,
630 value_in_quota_file: String::from(""),
631 value_in_period_file: String::from(""),
632 expected: Err(std::io::Error::new(
633 std::io::ErrorKind::InvalidData,
634 "Invalid format in Cgroup CPU interface file",
635 )),
636 },
637 ),
638 (
639 "Invalid-string-value-in-file",
640 TestCase {
641 file_exists: true,
642 value_in_quota_file: String::from("10000"),
643 value_in_period_file: String::from("test-string "),
644 expected: Err(std::io::Error::new(
645 std::io::ErrorKind::InvalidData,
646 "not a number",
647 )),
648 },
649 ),
650 (
651 "negative-value-in-file",
652 TestCase {
653 file_exists: true,
654 value_in_quota_file: String::from("-2"),
655 value_in_period_file: String::from("20000"),
656 expected: Ok(thread::available_parallelism().unwrap().get() as f32),
657 },
658 ),
659 (
660 "file-not-exist",
661 TestCase {
662 file_exists: false,
663 value_in_quota_file: String::from("10000 20000"),
664 value_in_period_file: String::from("10000 20000"),
665 expected: Err(std::io::Error::new(
666 std::io::ErrorKind::NotFound,
667 "File not found",
668 )),
669 },
670 ),
671 ]);
672 for tc in test_cases {
673 let curr_test_case = &tc.1;
674 let mut quota_file: tempfile::NamedTempFile;
675 let mut period_file: tempfile::NamedTempFile;
676 let mut test_quota_file_path = String::from(DEFAULT_NON_EXISTENT_PATH);
677 let mut test_period_file_path = String::from(DEFAULT_NON_EXISTENT_PATH);
678 if curr_test_case.file_exists {
679 quota_file = tempfile::NamedTempFile::new()
680 .expect("Error encountered while creating file!");
681 quota_file
682 .as_file_mut()
683 .write_all(curr_test_case.value_in_quota_file.as_bytes())
684 .expect("Error while writing to file");
685 test_quota_file_path = String::from(quota_file.path().to_str().unwrap());
686
687 period_file = tempfile::NamedTempFile::new()
688 .expect("Error encountered while creating file!");
689 period_file
690 .as_file_mut()
691 .write_all(curr_test_case.value_in_period_file.as_bytes())
692 .expect("Error while writing to file");
693 test_period_file_path = String::from(period_file.path().to_str().unwrap());
694 }
695 match cpu::get_cpu_limit_v1(
696 &test_quota_file_path,
697 &test_period_file_path,
698 get_system_cpu(),
699 ) {
700 Ok(int_val) => assert_eq!(
701 &int_val,
702 curr_test_case.expected.as_ref().unwrap(),
703 "{:?}",
704 tc
705 ),
706 Err(e) => assert_eq!(
707 e.kind(),
708 curr_test_case.expected.as_ref().unwrap_err().kind()
709 ),
710 }
711 }
712 }
713
714 #[test]
715 fn test_get_cpu_limit_v2() {
716 struct TestCase {
717 file_exists: bool,
718 value_in_file: String,
719 expected: Result<f32, std::io::Error>,
720 }
721
722 let test_cases = HashMap::from([
723 (
724 "valid-values-in-file",
725 TestCase {
726 file_exists: true,
727 value_in_file: String::from("10000 20000"),
728 expected: Ok(10000.0 / 20000.0),
729 },
730 ),
731 (
732 "Invalid-single-value-in-file",
733 TestCase {
734 file_exists: true,
735 value_in_file: String::from("10000"),
736 expected: Err(std::io::Error::new(
737 std::io::ErrorKind::InvalidData,
738 "Invalid format in Cgroup CPU interface file",
739 )),
740 },
741 ),
742 (
743 "Invalid-string-value-in-file",
744 TestCase {
745 file_exists: true,
746 value_in_file: String::from("10000 test-string "),
747 expected: Err(std::io::Error::new(
748 std::io::ErrorKind::InvalidData,
749 "not a number",
750 )),
751 },
752 ),
753 (
754 "max-value-in-file",
755 TestCase {
756 file_exists: true,
757 value_in_file: String::from("max 20000"),
758 expected: Ok(thread::available_parallelism().unwrap().get() as f32),
759 },
760 ),
761 (
762 "file-not-exist",
763 TestCase {
764 file_exists: false,
765 value_in_file: String::from(""),
766 expected: Err(std::io::Error::new(
767 std::io::ErrorKind::NotFound,
768 "File not found",
769 )),
770 },
771 ),
772 ]);
773 for tc in test_cases {
774 let curr_test_case = &tc.1;
775 let mut file: tempfile::NamedTempFile;
776 let mut test_file_path = String::from(DEFAULT_NON_EXISTENT_PATH);
777 if curr_test_case.file_exists {
778 file = tempfile::NamedTempFile::new()
779 .expect("Error encountered while creating file!");
780 file.as_file_mut()
781 .write_all(curr_test_case.value_in_file.as_bytes())
782 .expect("Error while writing to file");
783 test_file_path = String::from(file.path().to_str().unwrap())
784 }
785 match cpu::get_cpu_limit_v2(&test_file_path, get_system_cpu()) {
786 Ok(int_val) => assert_eq!(&int_val, curr_test_case.expected.as_ref().unwrap()),
787 Err(e) => assert_eq!(
788 e.kind(),
789 curr_test_case.expected.as_ref().unwrap_err().kind()
790 ),
791 }
792 }
793 }
794
795 #[test]
796 fn test_parse_controller_enable_file_for_cgroup_v2() {
797 struct TestCase {
798 file_exists: bool,
799 value_in_file: String,
800 controller_type: Controller,
801 expected: bool,
802 }
803
804 let test_cases = HashMap::from([
805 (
806 "cpu-enabled",
807 TestCase {
808 file_exists: true,
809 value_in_file: String::from("cpu memory IO"),
810 controller_type: Controller::Cpu,
811 expected: true,
812 },
813 ),
814 (
815 "memory-enabled",
816 TestCase {
817 file_exists: true,
818 value_in_file: String::from("cpu memory IO"),
819 controller_type: Controller::Memory,
820 expected: true,
821 },
822 ),
823 (
824 "memory-disabled",
825 TestCase {
826 file_exists: true,
827 value_in_file: String::from("cpu IO"),
828 controller_type: Controller::Memory,
829 expected: false,
830 },
831 ),
832 (
833 "cpu-disabled",
834 TestCase {
835 file_exists: true,
836 value_in_file: String::from("memory IO"),
837 controller_type: Controller::Cpu,
838 expected: false,
839 },
840 ),
841 (
842 "Invalid-value-in-file",
843 TestCase {
844 file_exists: true,
845 value_in_file: String::from("test-string test-string"),
846 controller_type: Controller::Cpu,
847 expected: false,
848 },
849 ),
850 (
851 "controller-file-not-exist",
852 TestCase {
853 file_exists: false,
854 value_in_file: String::from(""),
855 controller_type: Controller::Memory,
856 expected: false,
857 },
858 ),
859 ]);
860
861 for tc in test_cases {
862 let curr_test_case = &tc.1;
863 let controller_name: &str = match curr_test_case.controller_type {
864 Controller::Cpu => "cpu",
865 Controller::Memory => "memory",
866 };
867 let mut file: tempfile::NamedTempFile;
868 let mut test_file_path = String::from(DEFAULT_NON_EXISTENT_PATH);
869 if curr_test_case.file_exists {
870 file = tempfile::NamedTempFile::new()
871 .expect("Error encountered while creating file!");
872 file.as_file_mut()
873 .write_all(curr_test_case.value_in_file.as_bytes())
874 .expect("Error while writing to file");
875 test_file_path = String::from(file.path().to_str().unwrap())
876 }
877 assert_eq!(
878 parse_controller_enable_file_for_cgroup_v2(&test_file_path, controller_name),
879 curr_test_case.expected
880 );
881 }
882 }
883 }
884}