docker_helper.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import os
  2. import socket
  3. import fnmatch
  4. import subprocess
  5. import multiprocessing
  6. import json
  7. import docker
  8. from threading import Thread
  9. from toolset.utils import setup_util
  10. from toolset.utils.output_helper import tee_output
  11. from toolset.utils.metadata_helper import gather_tests
  12. def clean():
  13. '''
  14. Cleans all the docker images from the system
  15. '''
  16. subprocess.check_call(["docker", "image", "prune", "-f"])
  17. docker_ids = subprocess.check_output(["docker", "images",
  18. "-q"]).splitlines()
  19. for docker_id in docker_ids:
  20. subprocess.check_call(["docker", "image", "rmi", "-f", docker_id])
  21. subprocess.check_call(["docker", "system", "prune", "-a", "-f"])
  22. def build(benchmarker_config, test_names, out):
  23. '''
  24. Builds the dependency chain as well as the test implementation docker images
  25. for the given tests.
  26. '''
  27. tests = gather_tests(test_names)
  28. for test in tests:
  29. docker_buildargs = {
  30. 'CPU_COUNT': str(multiprocessing.cpu_count()),
  31. 'MAX_CONCURRENCY': str(max(benchmarker_config.concurrency_levels)),
  32. 'TFB_DATABASE': str(benchmarker_config.database_host)
  33. }
  34. test_docker_files = ["%s.dockerfile" % test.name]
  35. if test.docker_files is not None:
  36. if type(test.docker_files) is list:
  37. test_docker_files.extend(test.docker_files)
  38. else:
  39. raise Exception(
  40. "docker_files in benchmark_config.json must be an array")
  41. for test_docker_file in test_docker_files:
  42. deps = list(
  43. reversed(
  44. gather_dependencies(
  45. os.path.join(test.directory, test_docker_file))))
  46. docker_dir = os.path.join(setup_util.get_fwroot(), "toolset",
  47. "setup", "docker")
  48. for dependency in deps:
  49. docker_file = os.path.join(test.directory,
  50. dependency + ".dockerfile")
  51. if not docker_file or not os.path.exists(docker_file):
  52. docker_file = find(docker_dir, dependency + ".dockerfile")
  53. if not docker_file:
  54. tee_output(
  55. out,
  56. "Docker build failed; %s could not be found; terminating\n"
  57. % (dependency + ".dockerfile"))
  58. return 1
  59. # Build the dependency image
  60. try:
  61. for line in docker.APIClient(
  62. base_url='unix://var/run/docker.sock').build(
  63. path=os.path.dirname(docker_file),
  64. dockerfile="%s.dockerfile" % dependency,
  65. tag="tfb/%s" % dependency,
  66. buildargs=docker_buildargs,
  67. forcerm=True):
  68. prev_line = os.linesep
  69. if line.startswith('{"stream":'):
  70. line = json.loads(line)
  71. line = line[line.keys()[0]].encode('utf-8')
  72. if prev_line.endswith(os.linesep):
  73. tee_output(out, line)
  74. else:
  75. tee_output(out, line)
  76. prev_line = line
  77. except Exception as e:
  78. tee_output(out,
  79. "Docker dependency build failed; terminating\n")
  80. print(e)
  81. return 1
  82. # Build the test images
  83. for test_docker_file in test_docker_files:
  84. try:
  85. for line in docker.APIClient(
  86. base_url='unix://var/run/docker.sock').build(
  87. path=test.directory,
  88. dockerfile=test_docker_file,
  89. tag="tfb/test/%s" % test_docker_file.replace(
  90. ".dockerfile", ""),
  91. buildargs=docker_buildargs,
  92. forcerm=True):
  93. prev_line = os.linesep
  94. if line.startswith('{"stream":'):
  95. line = json.loads(line)
  96. line = line[line.keys()[0]].encode('utf-8')
  97. if prev_line.endswith(os.linesep):
  98. tee_output(out, line)
  99. else:
  100. tee_output(out, line)
  101. prev_line = line
  102. except Exception as e:
  103. tee_output(out, "Docker build failed; terminating\n")
  104. print(e)
  105. return 1
  106. def run(benchmarker_config, docker_files, out):
  107. '''
  108. Run the given Docker container(s)
  109. '''
  110. client = docker.from_env()
  111. for docker_file in docker_files:
  112. try:
  113. def watch_container(container):
  114. for line in container.logs(stream=True):
  115. tee_output(out, line)
  116. extra_hosts = {
  117. socket.gethostname(): str(benchmarker_config.server_host),
  118. 'TFB-SERVER': str(benchmarker_config.server_host),
  119. 'TFB-DATABASE': str(benchmarker_config.database_host),
  120. 'TFB-CLIENT': str(benchmarker_config.client_host)
  121. }
  122. container = client.containers.run(
  123. "tfb/test/%s" % docker_file.replace(".dockerfile", ""),
  124. network_mode="host",
  125. privileged=True,
  126. stderr=True,
  127. detach=True,
  128. extra_hosts=extra_hosts)
  129. watch_thread = Thread(target=watch_container, args=(container, ))
  130. watch_thread.daemon = True
  131. watch_thread.start()
  132. except Exception as e:
  133. tee_output(out,
  134. "Running docker cointainer: %s failed" % docker_file)
  135. print(e)
  136. return 1
  137. def find(path, pattern):
  138. '''
  139. Finds and returns all the the files matching the given pattern recursively in
  140. the given path.
  141. '''
  142. for root, dirs, files in os.walk(path):
  143. for name in files:
  144. if fnmatch.fnmatch(name, pattern):
  145. return os.path.join(root, name)
  146. def gather_dependencies(docker_file):
  147. '''
  148. Gathers all the known docker dependencies for the given docker image.
  149. '''
  150. # Avoid setting up a circular import
  151. from toolset.utils import setup_util
  152. deps = []
  153. docker_dir = os.path.join(setup_util.get_fwroot(), "toolset", "setup",
  154. "docker")
  155. if os.path.exists(docker_file):
  156. with open(docker_file) as fp:
  157. for line in fp.readlines():
  158. tokens = line.strip().split(' ')
  159. if tokens[0] == "FROM":
  160. # This is magic that our base image points to
  161. if tokens[1] != "ubuntu:16.04":
  162. depToken = tokens[1].strip().split(':')[
  163. 0].strip().split('/')[1]
  164. deps.append(depToken)
  165. dep_docker_file = os.path.join(
  166. os.path.dirname(docker_file),
  167. depToken + ".dockerfile")
  168. if not os.path.exists(dep_docker_file):
  169. dep_docker_file = find(docker_dir,
  170. depToken + ".dockerfile")
  171. deps.extend(gather_dependencies(dep_docker_file))
  172. return deps