podman.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import subprocess
  2. from pathlib import Path
  3. class Podman:
  4. @classmethod
  5. def run(
  6. cls,
  7. image: str,
  8. name: str,
  9. *,
  10. hostname: str,
  11. env: list,
  12. volumes: list[str],
  13. # TODO: Actually a list of Secret but creates a circular dependency
  14. secrets: list,
  15. ssh_auth_sock: Path | None = None,
  16. detach=True,
  17. ):
  18. args = ["run"]
  19. args += ["-h", hostname]
  20. args += ["--name", name]
  21. for e in env:
  22. args += ["-e", e]
  23. if ssh_auth_sock:
  24. args += ["-e", "SSH_AUTH_SOCK"]
  25. volumes += [f"{ssh_auth_sock}:{ssh_auth_sock}:Z"]
  26. args += ["--security-opt=label=disable"]
  27. if detach:
  28. args += ["--detach"]
  29. args += [a for vol in volumes for a in ["-v", vol]]
  30. args += [a for s in secrets for a in ["--secret", f"{s.name},mode=0{s.mode:o}"]]
  31. args += [image]
  32. cls._check_output(args)
  33. @classmethod
  34. def rm(cls, name, *, force=True):
  35. args = ["rm"]
  36. if force:
  37. args += ["-f"]
  38. args += [name]
  39. cls._check_output(args)
  40. @classmethod
  41. def exec(cls, name, /, *cmd, interactive):
  42. args = ["exec"]
  43. common_suffix = [name] + list(cmd)
  44. if interactive:
  45. args += ["-ti"] + common_suffix
  46. cls._run(args)
  47. else:
  48. args += common_suffix
  49. cls._check_output(args)
  50. @classmethod
  51. def secret_create(
  52. cls,
  53. name: str,
  54. *,
  55. replace=True,
  56. value: bytes | None = None,
  57. host_path: Path | None = None,
  58. ):
  59. if value and host_path:
  60. raise ValueError("both value and host_path can not be set")
  61. args = ["secret", "create"]
  62. kwargs = {}
  63. if replace:
  64. args += ["--replace"]
  65. args += [name]
  66. if value is not None:
  67. args += ["-"]
  68. kwargs["input"] = value
  69. elif host_path is not None:
  70. args += [host_path]
  71. cls._check_output(args, **kwargs)
  72. @classmethod
  73. def machine_is_running(cls) -> bool:
  74. try:
  75. subprocess.run(["podman", "ps"], capture_output=True, check=True)
  76. except subprocess.CalledProcessError as e:
  77. if "Cannot connect to Podman" in e.stderr.decode():
  78. return False
  79. return True
  80. @classmethod
  81. def machine_start(cls):
  82. cls._check_output(["machine", "start"])
  83. @classmethod
  84. def _run(cls, args: list, **kwargs):
  85. args = ["podman"] + args
  86. print(f"Executing `{" ".join(args)}`")
  87. return subprocess.run(args, **kwargs)
  88. @classmethod
  89. def _check_output(cls, args: list, **kwargs):
  90. args = ["podman"] + args
  91. print(f"Executing `{" ".join(args)}`")
  92. def run_and_output():
  93. out = subprocess.check_output(args, **kwargs)
  94. print(out.decode())
  95. try:
  96. run_and_output()
  97. except subprocess.CalledProcessError as e:
  98. if not cls.machine_is_running():
  99. print("podman not running, starting it")
  100. cls.machine_start()
  101. run_and_output()
  102. else:
  103. raise e