sort_services.py 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. from compose.config.errors import DependencyError
  2. def get_service_name_from_network_mode(network_mode):
  3. return get_source_name_from_network_mode(network_mode, 'service')
  4. def get_container_name_from_network_mode(network_mode):
  5. return get_source_name_from_network_mode(network_mode, 'container')
  6. def get_source_name_from_network_mode(network_mode, source_type):
  7. if not network_mode:
  8. return
  9. if not network_mode.startswith(source_type+':'):
  10. return
  11. _, net_name = network_mode.split(':', 1)
  12. return net_name
  13. def get_service_names(links):
  14. return [link.split(':')[0] for link in links]
  15. def get_service_names_from_volumes_from(volumes_from):
  16. return [volume_from.source for volume_from in volumes_from]
  17. def get_service_dependents(service_dict, services):
  18. name = service_dict['name']
  19. return [
  20. service for service in services
  21. if (name in get_service_names(service.get('links', [])) or
  22. name in get_service_names_from_volumes_from(service.get('volumes_from', [])) or
  23. name == get_service_name_from_network_mode(service.get('network_mode')) or
  24. name == get_service_name_from_network_mode(service.get('pid')) or
  25. name in service.get('depends_on', []))
  26. ]
  27. def sort_service_dicts(services):
  28. # Topological sort (Cormen/Tarjan algorithm).
  29. unmarked = services[:]
  30. temporary_marked = set()
  31. sorted_services = []
  32. def visit(n):
  33. if n['name'] in temporary_marked:
  34. if n['name'] in get_service_names(n.get('links', [])):
  35. raise DependencyError('A service can not link to itself: %s' % n['name'])
  36. if n['name'] in n.get('volumes_from', []):
  37. raise DependencyError('A service can not mount itself as volume: %s' % n['name'])
  38. if n['name'] in n.get('depends_on', []):
  39. raise DependencyError('A service can not depend on itself: %s' % n['name'])
  40. raise DependencyError('Circular dependency between %s' % ' and '.join(temporary_marked))
  41. if n in unmarked:
  42. temporary_marked.add(n['name'])
  43. for m in get_service_dependents(n, services):
  44. visit(m)
  45. temporary_marked.remove(n['name'])
  46. unmarked.remove(n)
  47. sorted_services.insert(0, n)
  48. while unmarked:
  49. visit(unmarked[-1])
  50. return sorted_services