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.

185 lines
5.6KB

  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. from .zfs import *
  8. import os
  9. import yaml
  10. from .steps import create_step
  11. from .snapshot import new_snapshot
  12. from tabulate import tabulate
  13. import subprocess
  14. from .misc import find_prefix
  15. def validate_spec(spec):
  16. if 'base' not in spec:
  17. raise ValueError('Missing base in specification')
  18. if 'steps' not in spec:
  19. raise ValueError('Missing steps in specification')
  20. def build_squeeze(spec, args):
  21. validate_spec(spec)
  22. base = spec['base']
  23. base, sha256 = zfs_find(base, focker_type='image', zfs_type='snapshot')
  24. root = '/'.join(base.split('/')[:-1])
  25. print('base:', base, 'root:', root)
  26. steps = spec['steps']
  27. if not isinstance(steps, list):
  28. steps = [ steps ]
  29. for st in steps:
  30. st = create_step(st)
  31. sha256 = st.hash(sha256, args=args)
  32. if zfs_exists_snapshot_sha256(sha256):
  33. name = zfs_snapshot_by_sha256(sha256)
  34. print('Reusing:', name)
  35. return (name, sha256)
  36. name = find_prefix(root + '/', sha256)
  37. def atomic():
  38. for st in steps:
  39. st = create_step(st)
  40. st.execute(zfs_mountpoint(name), args=args)
  41. zfs_set_props(name,
  42. { 'focker:sha256': sha256 })
  43. name = new_snapshot(base, atomic, name)
  44. return (name, sha256)
  45. def build(spec, args):
  46. validate_spec(spec)
  47. base = spec['base']
  48. base, base_sha256 = zfs_find(base, focker_type='image', zfs_type='snapshot')
  49. root = '/'.join(base.split('/')[:-1])
  50. print('base:', base, 'root:', root)
  51. steps = spec['steps']
  52. if not isinstance(steps, list):
  53. steps = [ steps ]
  54. for st in steps:
  55. st = create_step(st)
  56. st_sha256 = st.hash(base_sha256, args=args)
  57. if zfs_exists_snapshot_sha256(st_sha256):
  58. base = zfs_snapshot_by_sha256(st_sha256)
  59. base_sha256 = st_sha256
  60. print('Reusing:', base)
  61. continue
  62. for pre in range(7, 64):
  63. name = root + '/' + st_sha256[:pre]
  64. if not zfs_exists(name):
  65. break
  66. feed = {
  67. 'focker:sha256': st_sha256
  68. }
  69. def atomic():
  70. st.execute(zfs_mountpoint(name), args=args)
  71. zfs_set_props(name, feed)
  72. snap_name = new_snapshot(base, atomic, name)
  73. # zfs_set_props(name, feed)
  74. # zfs_set_props(snap_name, feed)
  75. base = snap_name
  76. base_sha256 = st_sha256
  77. return (base, base_sha256)
  78. def command_image_build(args):
  79. # os.chdir(args.focker_dir)
  80. fname = os.path.join(args.focker_dir, 'Fockerfile')
  81. print('fname:', fname)
  82. if not os.path.exists(fname):
  83. raise ValueError('No Fockerfile could be found in the specified directory')
  84. with open(fname, 'r') as f:
  85. spec = yaml.safe_load(f)
  86. print('spec:', spec)
  87. image, image_sha256 = build_squeeze(spec, args) \
  88. if args.squeeze else build(spec, args)
  89. zfs_untag(args.tags)
  90. zfs_tag(image.split('@')[0], args.tags)
  91. def command_image_tag(args):
  92. zfs_untag(args.tags, focker_type='image')
  93. name, _ = zfs_find(args.reference, focker_type='image', zfs_type='filesystem')
  94. zfs_tag(name, args.tags)
  95. def command_image_untag(args):
  96. zfs_untag(args.tags, focker_type='image')
  97. def command_image_list(args):
  98. lst = zfs_list(fields=['name', 'refer', 'focker:sha256', 'focker:tags', 'origin'],
  99. focker_type='image')
  100. # zfs_parse_output(['zfs', 'list', '-o', 'name,refer,focker:sha256,focker:tags,origin', '-H'])
  101. lst = filter(lambda a: a[2] != '-', lst)
  102. if args.tagged_only:
  103. lst = filter(lambda a: a[3] != '-', lst)
  104. lst = list(lst)
  105. lst = list(map(lambda a: [ a[3], a[1],
  106. a[2] if args.full_sha256 else a[2][:7],
  107. a[4].split('/')[-1].split('@')[0] ], lst))
  108. print(tabulate(lst, headers=['Tags', 'Size', 'SHA256', 'Base']))
  109. def command_image_prune(args):
  110. poolname = zfs_poolname()
  111. again = True
  112. while again:
  113. again = False
  114. fields=['focker:sha256', 'focker:tags', 'origin', 'name']
  115. lst = zfs_list(fields=fields, focker_type='image')
  116. lst += zfs_list(fields=fields, focker_type='jail')
  117. # lst = zfs_parse_output(['zfs', 'list', '-o', 'focker:sha256,focker:tags,origin,name', '-H', '-r', poolname + '/focker/images'])
  118. used = set()
  119. for r in lst:
  120. if r[2] == '-':
  121. continue
  122. used.add(r[2].split('@')[0])
  123. for r in lst:
  124. if r[0] == '-' or r[1] != '-':
  125. continue
  126. if r[3] not in used:
  127. print('Removing:', r[3])
  128. zfs_run(['zfs', 'destroy', '-r', '-f', r[3]])
  129. again = True
  130. # zfs_parse_output(['zfs'])
  131. def command_image_remove(args):
  132. try:
  133. snap, snap_sha256 = zfs_find(args.reference, focker_type='image',
  134. zfs_type='snapshot')
  135. except AmbiguousValueError:
  136. raise
  137. except ValueError:
  138. if args.force:
  139. return
  140. raise
  141. ds = snap.split('@')[0]
  142. command = ['zfs', 'destroy', '-r', '-f']
  143. #if args.remove_children:
  144. # command.append('-r')
  145. if args.remove_dependents:
  146. command.append('-R')
  147. command.append(ds)
  148. res = subprocess.run(command)
  149. if res.returncode != 0:
  150. raise RuntimeError('zfs destroy failed')
  151. # zfs_run(['zfs', 'destroy', ds])