start.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import os
  2. import sys
  3. import subprocess
  4. import socket
  5. from pathlib import Path, PurePosixPath
  6. from dataclasses import dataclass
  7. is_windows = os.name == "nt"
  8. def read_data_sources(hostname: str, login: str) -> list[Path]:
  9. file = Path(f"./data_sources_{hostname}_{login}")
  10. with open(file) as f:
  11. paths = f.readlines()
  12. return [Path(p_str.strip()).expanduser() for p_str in paths]
  13. @dataclass
  14. class Secret:
  15. host_path: Path
  16. name: str
  17. mode: int
  18. def create(self):
  19. args = ["podman", "secret", "create", "--replace", self.name, self.host_path]
  20. print(args)
  21. subprocess.run(args)
  22. @classmethod
  23. def from_line(cls, line: str):
  24. path = Path(line).expanduser()
  25. return cls(host_path=path, name=path.name, mode=0o0400)
  26. @classmethod
  27. def read_sources(cls, hostname: str, login: str) -> list["Secret"]:
  28. file = Path(f"./secret_sources_{hostname}_{login}")
  29. with open(file) as f:
  30. lines = f.readlines()
  31. return [cls.from_line(l.strip()) for l in lines]
  32. def to_source_path(path: Path):
  33. mount_base = PurePosixPath("/mnt") / "source"
  34. inner_path = PurePosixPath(path)
  35. with_drive = PurePosixPath(inner_path.parts[0].replace(":", "")).joinpath(*inner_path.parts[1:])
  36. return mount_base / with_drive.relative_to(with_drive.anchor)
  37. def create_secrets(secret_sources: list[Secret]):
  38. for s in secret_sources:
  39. s.create()
  40. def start_borgmatic_container(hostname: str, login: str, secret_sources: list[Secret]):
  41. data_sources = read_data_sources(hostname, login)
  42. container_name = f"borgmatic_{login}"
  43. ssh_auth_sock = os.getenv("SSH_AUTH_SOCK")
  44. data_path = Path.cwd() / "data"
  45. config_d_path = data_path / "borgmatic.d"
  46. config_path = data_path / "borgmatic"
  47. history_file = data_path / ".bash_history"
  48. history_file.touch()
  49. ssh_config_path = Path.home() / ".ssh"
  50. volumes = [
  51. f"{config_d_path}:/etc/borgmatic.d/",
  52. f"{config_path}:/etc/borgmatic/",
  53. f"{ssh_config_path}:/root/.ssh",
  54. f"{history_file}:/root/.bash_history",
  55. "borg_config:/root/.config/borg",
  56. "borg_cache:/root/.cache/borg",
  57. "borgmatic_state:/root/.local/state/borgmatic",
  58. ]
  59. if ssh_auth_sock:
  60. volumes += [f"{ssh_auth_sock}:{ssh_auth_sock}:Z"]
  61. volumes += [
  62. f"{vol}:{to_source_path(vol)}:ro" for vol in data_sources
  63. ]
  64. volume_args = [a for vol in volumes for a in ["-v", vol]]
  65. secrets_args = [a for s in secret_sources for a in ["--secret", f"{s.name},mode=0{s.mode:o}"]]
  66. image_name = "ghcr.io/borgmatic-collective/borgmatic"
  67. args = [
  68. "podman",
  69. "run",
  70. "-h",
  71. hostname,
  72. "--detach",
  73. "--name",
  74. container_name,
  75. "-e",
  76. "SSH_AUTH_SOCK",
  77. "-e",
  78. "TZ=Europe/Paris",
  79. "-e",
  80. "SSH_KEY_NAME",
  81. "-e",
  82. f"HOST_LOGIN={login}",
  83. "--security-opt=label=disable"
  84. ] + volume_args + secrets_args + [image_name]
  85. print(args)
  86. subprocess.run(args)
  87. def main():
  88. login = os.getlogin()
  89. hostname = socket.gethostname()
  90. secret_sources = Secret.read_sources(hostname, login)
  91. if not secret_sources:
  92. print("no secret required ?")
  93. try:
  94. if sys.argv[1] == "create_secrets":
  95. create_secrets(secret_sources)
  96. elif sys.argv[1] == "start":
  97. start_borgmatic_container(hostname, login, secret_sources)
  98. elif sys.argv[1] == "rm":
  99. subprocess.run(["podman", "rm", "-f", f"borgmatic_{login}"])
  100. elif sys.argv[1] == "bash":
  101. subprocess.run(["podman", "exec", "-ti", f"borgmatic_{login}", "bash"])
  102. except IndexError:
  103. print("You should provide an argument")
  104. exit(1)
  105. if __name__ == "__main__":
  106. main()