base.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import json
  2. import sys
  3. import tempfile
  4. from pathlib import Path
  5. from metadocker.cmdline.base import ArgsFromFile
  6. from metadocker.common.errors import MetadockerError
  7. from metadocker.common.misc import get_root_dir
  8. from metadocker.db.db import settings
  9. from metadocker.docker import DockerBuilder
  10. from metadocker.common.var import VarDict, VarStr, VarPath, NotEmpty, Var, EmptyValue
  11. settings.load()
  12. class EnvHolder:
  13. def __init__(self):
  14. self._items = []
  15. def __getitem__(self, item):
  16. return getattr(self, item)
  17. def __setitem__(self, key, value):
  18. if key not in self._items:
  19. self._items.append(key)
  20. return setattr(self, key, value)
  21. def items(self):
  22. return [
  23. (k, getattr(self, k)) for k in self._items
  24. ]
  25. class Env:
  26. _current_env = None
  27. _suspend_run_ = False
  28. PROJECT : str
  29. PROJECT_VERSION : str
  30. DOCKER_TAG : str
  31. DOCKER_NAME : str
  32. ENV : dict
  33. PORTS : dict
  34. VOLUMES : dict
  35. COPY : dict
  36. APP_DIR : Path
  37. _vars_ = [
  38. VarStr("PROJECT", required=True, help="Le nom du projet", is_meta=True, ask=True),
  39. VarStr("PROJECT_VERSION", None, help="La version du projet", validate=NotEmpty,
  40. is_meta=True, null=True, ask=True),
  41. VarStr("DOCKER_TAG", None, is_meta=True, validate=NotEmpty,
  42. help="Nom du tag de l'image docker. Par défaut: {env.PROJECT.lower()}-{PROJECT_VERSION}", ask=True),
  43. VarStr("DOCKER_NAME", None, is_meta=True, validate=NotEmpty,
  44. help="Nom du conteneur docker. Par défaut {env.PROJECT.lower()}", ask=True),
  45. VarDict("ENV", {}, is_meta=True, required=True, help="La liste des variable d'environnemnt à utiliser"),
  46. VarDict("PORTS", {8101: 8000}, required=True, is_meta=True,
  47. help="Le mapping des ports. En clé les ports de l'hote et en valeur les port du container"),
  48. VarDict("VOLUMES", {"./data": "/data"}, required=True,
  49. help="Le mapping des ports. En clé les ports de l'hote et en valeur les port du container",
  50. is_meta=True, null=True),
  51. VarDict("COPY", {}, required=True, is_meta=True,
  52. help="Dictionaire des fichier a copier: cle: fs hote valeur fs invite"),
  53. VarPath("APP_DIR", "/data", required=True, is_meta=True,
  54. validate=NotEmpty, help="Le dossier ou est lancé le projet", ask=True),
  55. ]
  56. _name_ = None
  57. _alias_ = []
  58. class EnvException(MetadockerError):
  59. def __init__(self, errors):
  60. super().__init__(errors)
  61. self.errors = errors
  62. def get_error(self):
  63. return "\n".join(repr(x) for x in self.errors)
  64. def __init__(self, debug=False, **kwargs):
  65. Env._current_env = self
  66. self.root = get_root_dir()
  67. self.debug = debug
  68. self.meta = None
  69. self.docker : DockerBuilder
  70. self.env = None
  71. for k, v in kwargs.items():
  72. setattr(self, k, v)
  73. def init(self):
  74. self.meta = self.get_meta()
  75. self.docker = DockerBuilder(
  76. self.meta.PROJECT,
  77. self.meta.PROJECT_VERSION,
  78. self.meta.DOCKER_TAG,
  79. self.meta.DOCKER_NAME,
  80. )
  81. def read(self):
  82. self.init()
  83. self.env = self.get_env()
  84. def _validate_fields(self, filter=None):
  85. data= EnvHolder()
  86. errors = []
  87. for var in self._vars_:
  88. if filter and not filter(var): continue
  89. try:
  90. value = var.run_validation(self)
  91. except Var.ByPassException:
  92. value = None
  93. except Var.VarException as err:
  94. errors.append(err)
  95. continue
  96. data[var.name] = value
  97. if errors:
  98. raise self.EnvException(errors)
  99. for k,v in data.items():
  100. setattr(data, k, v)
  101. setattr(self, k, v)
  102. return data
  103. def get_meta(self):
  104. return self._validate_fields(lambda x: x.is_meta)
  105. def get_env(self):
  106. return self._validate_fields()
  107. def generate_docker_file(self ):
  108. raise NotImplementedError()
  109. def build_docker_file(self, output=None):
  110. if not output:
  111. temp = tempfile.TemporaryDirectory()
  112. output = Path(temp.name) / "Dockerfile"
  113. output = Path(output)
  114. output.parent.mkdir(exist_ok=True, parents=True)
  115. self.generate_docker_file()
  116. output.write_text(self.docker.get_file_content())
  117. def create_empty_config(self, values=None, interractive=True):
  118. def _value(v, field):
  119. if v is EmptyValue:
  120. return f"# type in ({', '.join([x.__name__ for x in field.expected_types] or ['*'])})"
  121. if v is None:
  122. return "None"
  123. if isinstance(v, Path):
  124. v = str(v)
  125. if isinstance(v, (str, list, int, float, dict)):
  126. if v == {}: return "{\n}"
  127. if v == []: return "[\n]"
  128. return json.dumps(v, indent=2)
  129. return v
  130. def _help(v):
  131. return v.replace("\n", "\n#")
  132. values = values or {}
  133. if interractive:
  134. self.ask_for_data(values)
  135. data = [
  136. "#!/bin/env python3",
  137. f"from metadocker.env import {self.__class__.__name__}",
  138. f"env = {self.__class__.__name__}()",
  139. f""
  140. ]
  141. for var in self._vars_:
  142. if var.help:
  143. data.append(f"# {var.help}")
  144. value = _value(getattr(self, var.name, var.default_value), var)
  145. if var.required or var.ask:
  146. data.append(f"env.{var.name}={value}")
  147. else:
  148. data.append(f"# env.{var.name}={value}")
  149. data.append("")
  150. data.append("# lancement ")
  151. data.append("env.main()")
  152. return "\n".join(data)
  153. def main(self, args=None):
  154. if Env._suspend_run_: return
  155. self.init()
  156. ArgsFromFile.main(self, args)
  157. def ask_for_data(self, values = None):
  158. values = values or {}
  159. for var in self._vars_:
  160. if var.ask:
  161. kwargs = {}
  162. if var.name in values:
  163. kwargs["value"] = values[var.name]
  164. value = var.ask_value(**kwargs)
  165. setattr(self, var.name, value)