sort_services.py 1.8 KB

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