utils.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. from apps.deploy.models import ImageConfig, DeployMenu
  2. from apps.configuration.models import ConfigKey, ConfigValue
  3. from apps.apis.utils import MapConfigValues
  4. from libs.utils import Container
  5. from libs.tools import is_valid_ip
  6. from collections import defaultdict
  7. import base64
  8. import uuid
  9. import os
  10. def add_file_to_tar(tar, file):
  11. tarinfo = tar.gettarinfo(file, os.path.basename(file))
  12. tarinfo.mode = 0o755
  13. with open(file, "rb") as f:
  14. tar.addfile(tarinfo, f)
  15. def create_container(ctr: Container, pro, env, **kwargs):
  16. env_configs = {x.name: x.value for x in ImageConfig.query.filter_by(img_id=pro.image_id).all()}
  17. config_keys = ConfigKey.query.filter_by(owner_type='app', owner_id=pro.id, type='system').all()
  18. config_values = ConfigValue.query.filter(
  19. ConfigValue.key_id.in_([x.id for x in config_keys])).all() if config_keys else []
  20. menu_start = DeployMenu.query.filter_by(app_id=pro.id, name='容器启动').first()
  21. map_values = MapConfigValues(config_values, env.id)
  22. app_configs = {x.name: map_values.get(x.id) for x in config_keys}
  23. hostname = app_configs.pop('__HOST_NAME', None) or None
  24. host_config = dict(
  25. mem_limit=app_configs.pop('__MEM_LIMIT', None) or None,
  26. network_mode=app_configs.pop('__NETWORK_MODE', None) or 'default',
  27. dns=app_configs.pop('__DNS_SERVER').split(';') if app_configs.get('__DNS_SERVER') else None,
  28. restart_policy={'Name': 'always'}
  29. )
  30. expose_port = app_configs.pop('__EXPOSE_PORT', None)
  31. if expose_port and host_config['network_mode'] == 'default':
  32. kwargs.setdefault('ports', [])
  33. host_config['port_bindings'] = defaultdict(list)
  34. for item in expose_port.split(';'):
  35. value = item.split(':') if item.count(':') == 2 else ['0.0.0.0'] + item.split(':')
  36. if value[2] not in kwargs['ports']:
  37. kwargs['ports'].append(value[2])
  38. host_config['port_bindings'][value[2]].append(tuple(value[:2]))
  39. bind_volume = app_configs.pop('__BIND_VOLUME', None)
  40. if bind_volume:
  41. kwargs.setdefault('volumes', [])
  42. host_config.setdefault('binds', [])
  43. for item in bind_volume.split(';'):
  44. kwargs['volumes'].append(item.split(':')[1])
  45. host_config['binds'].append(item)
  46. ctr.create_host_config(**host_config)
  47. env_configs.update(app_configs)
  48. env_configs['__API_TOKEN'] = uuid.uuid4().hex
  49. env_configs['__DEPLOY_START'] = base64.b64encode(menu_start.command.encode()).decode()
  50. ctr.create(
  51. name=ctr.name,
  52. hostname=hostname,
  53. entrypoint='/entrypoint.sh',
  54. environment=env_configs,
  55. **kwargs
  56. )
  57. return env_configs['__API_TOKEN']
  58. def valid_app_setting(name: str, values: list):
  59. ok, message = False, None
  60. if name == '__MEM_LIMIT':
  61. if all([x[:-1].isdigit() and (x.lower().endswith('m') or x.lower().endswith('g')) for x in values if x != '']):
  62. ok = True
  63. else:
  64. message = '请输入以【m|M|g|G】结尾的格式!'
  65. elif name == '__DNS_SERVER':
  66. if all([is_valid_ip(*x.split(';')) for x in values if x != '']):
  67. ok = True
  68. else:
  69. message = '请输入以分号分隔的一个或多个合法的IP地址!'
  70. elif name == '__NETWORK_MODE':
  71. if all([x in ['default', 'none', 'host'] for x in values if x != '']):
  72. ok = True
  73. else:
  74. message = '网络模式仅支持【default | none | host】!'
  75. elif name == '__EXPOSE_PORT':
  76. objects = []
  77. [objects.extend(x) for x in [x.split(';') for x in values if x != '']]
  78. try:
  79. for item in [x.split(':') for x in objects]:
  80. if len(item) == 3:
  81. assert is_valid_ip(item[0])
  82. assert 0 < int(item[1]) <= 65535
  83. assert 0 < int(item[2]) <= 65535
  84. elif len(item) == 2:
  85. assert 0 < int(item[0]) <= 65535
  86. assert 0 < int(item[1]) <= 65535
  87. else:
  88. assert False
  89. ok = True
  90. except (AssertionError, ValueError):
  91. message = '无效的映射端口,示例:127.0.0.1:3306:3306;80:5000'
  92. elif name == '__BIND_VOLUME':
  93. try:
  94. for item in [x.split(':') for x in values if x != '']:
  95. assert item[0].startswith('/')
  96. assert item[1].startswith('/')
  97. if len(item) == 3:
  98. assert item[3] in ['ro', 'rw']
  99. ok = True
  100. except (AssertionError, IndexError, ValueError):
  101. message = '无效的映射目录,示例:/home/user1:/mnt/vol1;/home/user2:/mnt/vol2:ro'
  102. elif name == '__HOST_NAME':
  103. ok = True
  104. else:
  105. message = '不支持的设置参数 ' + name
  106. return ok, message
  107. def get_built_in_menus(name=None):
  108. all_valid_menus = {
  109. '容器创建': {'name': '容器创建', 'desc': '预定义:容器被创建后执行,用于初始化容器', 'command': ''},
  110. '应用发布': {'name': '应用发布', 'desc': '预定义:点击发布按钮执行发布时执行', 'command': ''},
  111. '容器启动': {'name': '容器启动', 'desc': '预定义:容器启动时执行', 'command': ''}
  112. }
  113. if name is None:
  114. return all_valid_menus
  115. elif name in all_valid_menus:
  116. return all_valid_menus[name]
  117. else:
  118. raise TypeError('Invalid name <%r> for built in menus' % name)