IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an email to s dot adaszewski at gmail dot com. User accounts are meant only to report issues and/or generate pull requests. This is a purpose-specific Git hosting for ADARED projects. Thank you for your understanding!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
5.1KB

  1. #
  2. # Copyright (C) Stanislaw Adaszewski, 2020
  3. # License: GNU General Public License v3.0
  4. # URL: https://github.com/sadaszewski/focker
  5. # URL: https://adared.ch/focker
  6. #
  7. import os
  8. import yaml
  9. from .zfs import AmbiguousValueError, \
  10. zfs_find, \
  11. zfs_tag, \
  12. zfs_untag, \
  13. zfs_mountpoint, \
  14. zfs_poolname, \
  15. zfs_set_props
  16. from .jail import jail_fs_create, \
  17. jail_create, \
  18. jail_remove, \
  19. backup_file, \
  20. quote
  21. from .misc import random_sha256_hexdigest, \
  22. find_prefix
  23. import subprocess
  24. import jailconf
  25. import os
  26. from .misc import focker_lock, \
  27. focker_unlock
  28. import pdb
  29. def exec_prebuild(spec, path):
  30. if isinstance(spec, str):
  31. spec = [ spec ]
  32. if not isinstance(spec, list):
  33. raise ValueError('exec.prebuild should be a string or a list of strings')
  34. spec = ' && '.join(spec)
  35. print('Running exec.build command:', spec)
  36. spec = [ '/bin/sh', '-c', spec ]
  37. oldwd = os.getcwd()
  38. os.chdir(path)
  39. res = subprocess.run(spec)
  40. if res.returncode != 0:
  41. raise RuntimeError('exec.prebuild failed')
  42. os.chdir(oldwd)
  43. def build_volumes(spec):
  44. poolname = zfs_poolname()
  45. for tag, params in spec.items():
  46. name = None
  47. try:
  48. name, _ = zfs_find(tag, focker_type='volume')
  49. except ValueError:
  50. pass
  51. if name is None:
  52. sha256 = random_sha256_hexdigest()
  53. name = find_prefix(poolname + '/focker/volumes/', sha256)
  54. subprocess.check_output(['zfs', 'create', '-o', 'focker:sha256=' + sha256, name])
  55. zfs_untag([ tag ], focker_type='volume')
  56. zfs_tag(name, [ tag ])
  57. mountpoint = zfs_mountpoint(name)
  58. print('params:', params)
  59. if 'chown' in params:
  60. os.chown(mountpoint, *map(int, params['chown'].split(':')))
  61. if 'chmod' in params:
  62. os.chmod(mountpoint, params['chmod'])
  63. if 'zfs' in params:
  64. zfs_set_props(name, params['zfs'])
  65. def build_images(spec, path, args):
  66. # print('build_images(): NotImplementedError')
  67. for (tag, focker_dir) in spec.items():
  68. cmd = ['focker', 'image', 'build',
  69. os.path.join(path, focker_dir), '-t', tag]
  70. if args.squeeze:
  71. cmd.append('--squeeze')
  72. focker_unlock()
  73. res = subprocess.run(cmd)
  74. focker_lock()
  75. if res.returncode != 0:
  76. raise RuntimeError('Image build failed: ' + str(res.returncode))
  77. def setup_dependencies(spec, generated_names):
  78. if os.path.exists('/etc/jail.conf'):
  79. conf = jailconf.load('/etc/jail.conf')
  80. else:
  81. conf = jailconf.JailConf()
  82. for (jailname, jailspec) in spec.items():
  83. if 'depend' not in jailspec:
  84. continue
  85. depend = jailspec.get('depend', [])
  86. if isinstance(depend, str):
  87. depend = [ depend ]
  88. if not isinstance(depend, list):
  89. raise ValueError('depend must be a string or a list of strings')
  90. # pdb.set_trace()
  91. depend = list(map(lambda a: generated_names[a], depend))
  92. if len(depend) == 1:
  93. depend = depend[0]
  94. conf[generated_names[jailname]]['depend'] = \
  95. depend
  96. conf.write('/etc/jail.conf')
  97. def build_jails(spec):
  98. backup_file('/etc/jail.conf')
  99. generated_names = {}
  100. for (jailname, jailspec) in spec.items():
  101. try:
  102. name, _ = zfs_find(jailname, focker_type='jail')
  103. jail_remove(zfs_mountpoint(name))
  104. except AmbiguousValueError:
  105. raise
  106. except ValueError:
  107. pass
  108. name = jail_fs_create(jailspec['image'])
  109. zfs_untag([ jailname ], focker_type='jail')
  110. zfs_tag(name, [ jailname ])
  111. path = zfs_mountpoint(name)
  112. generated_names[jailname] = \
  113. jail_create(path,
  114. jailspec.get('exec.start', '/bin/sh /etc/rc'),
  115. jailspec.get('env', {}),
  116. [ [from_, on] \
  117. for (from_, on) in jailspec.get('mounts', {}).items() ],
  118. hostname=jailname,
  119. overrides={
  120. 'exec.stop': jailspec.get('exec.stop', '/bin/sh /etc/rc.shutdown'),
  121. 'ip4.addr': jailspec.get('ip4.addr', '127.0.1.0'),
  122. 'interface': jailspec.get('interface', 'lo1'),
  123. 'host.hostname': jailspec.get('host.hostname', jailname)
  124. })
  125. setup_dependencies(spec, generated_names)
  126. def command_compose_build(args):
  127. if not os.path.exists(args.filename):
  128. raise ValueError('File not found: ' + args.filename)
  129. path, _ = os.path.split(args.filename)
  130. print('path:', path)
  131. with open(args.filename, 'r') as f:
  132. spec = yaml.safe_load(f)
  133. if 'exec.prebuild' in spec:
  134. exec_prebuild(spec['exec.prebuild'], path)
  135. if 'volumes' in spec:
  136. build_volumes(spec['volumes'])
  137. if 'images' in spec:
  138. build_images(spec['images'], path, args)
  139. if 'jails' in spec:
  140. build_jails(spec['jails'])
  141. def command_compose_run(args):
  142. raise NotImplementedError