tmpfs.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. from typing import TYPE_CHECKING
  2. if TYPE_CHECKING:
  3. from container import Container
  4. from render import Render
  5. from storage import IxStorage
  6. try:
  7. from .error import RenderError
  8. from .validations import valid_fs_path_or_raise, valid_octal_mode_or_raise
  9. except ImportError:
  10. from error import RenderError
  11. from validations import valid_fs_path_or_raise, valid_octal_mode_or_raise
  12. class Tmpfs:
  13. def __init__(self, render_instance: "Render", container_instance: "Container"):
  14. self._render_instance = render_instance
  15. self._container_instance = container_instance
  16. self._tmpfs: dict = {}
  17. def add(self, mount_path: str, config: "IxStorage"):
  18. mount_path = valid_fs_path_or_raise(mount_path)
  19. if self.is_defined(mount_path):
  20. raise RenderError(f"Tmpfs mount path [{mount_path}] already added")
  21. if self._container_instance.storage.is_defined(mount_path):
  22. raise RenderError(f"Tmpfs mount path [{mount_path}] already used for another volume mount")
  23. mount_config = config.get("tmpfs_config", {})
  24. size = mount_config.get("size", None)
  25. mode = mount_config.get("mode", None)
  26. uid = mount_config.get("uid", None)
  27. gid = mount_config.get("gid", None)
  28. if size is not None:
  29. if not isinstance(size, int):
  30. raise RenderError(f"Expected [size] to be an integer for [tmpfs] type, got [{size}]")
  31. if not size > 0:
  32. raise RenderError(f"Expected [size] to be greater than 0 for [tmpfs] type, got [{size}]")
  33. # Convert Mebibytes to Bytes
  34. size = size * 1024 * 1024
  35. if mode is not None:
  36. mode = valid_octal_mode_or_raise(mode)
  37. if uid is not None and not isinstance(uid, int):
  38. raise RenderError(f"Expected [uid] to be an integer for [tmpfs] type, got [{uid}]")
  39. if gid is not None and not isinstance(gid, int):
  40. raise RenderError(f"Expected [gid] to be an integer for [tmpfs] type, got [{gid}]")
  41. self._tmpfs[mount_path] = {}
  42. if size is not None:
  43. self._tmpfs[mount_path]["size"] = str(size)
  44. if mode is not None:
  45. self._tmpfs[mount_path]["mode"] = str(mode)
  46. if uid is not None:
  47. self._tmpfs[mount_path]["uid"] = str(uid)
  48. if gid is not None:
  49. self._tmpfs[mount_path]["gid"] = str(gid)
  50. def is_defined(self, mount_path: str):
  51. return mount_path in self._tmpfs
  52. def has_tmpfs(self):
  53. return bool(self._tmpfs)
  54. def render(self):
  55. result = []
  56. for mount_path, config in self._tmpfs.items():
  57. opts = sorted([f"{k}={v}" for k, v in config.items()])
  58. result.append(f"{mount_path}:{','.join(opts)}" if opts else mount_path)
  59. return sorted(result)