migrate-compose-file-v1-to-v2.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. #!/usr/bin/env python
  2. """
  3. Migrate a Compose file from the V1 format in Compose 1.5 to the V2 format
  4. supported by Compose 1.6+
  5. """
  6. from __future__ import absolute_import
  7. from __future__ import unicode_literals
  8. import argparse
  9. import logging
  10. import sys
  11. import ruamel.yaml
  12. log = logging.getLogger('migrate')
  13. def migrate(content):
  14. data = ruamel.yaml.load(content, ruamel.yaml.RoundTripLoader)
  15. service_names = data.keys()
  16. for name, service in data.items():
  17. # remove links and external links
  18. service.pop('links', None)
  19. external_links = service.pop('external_links', None)
  20. if external_links:
  21. log.warn(
  22. "Service {name} has external_links: {ext}, which are no longer "
  23. "supported. See https://docs.docker.com/compose/networking/ "
  24. "for options on how to connect external containers to the "
  25. "compose network.".format(name=name, ext=external_links))
  26. # net is now networks
  27. if 'net' in service:
  28. service['networks'] = [service.pop('net')]
  29. # create build section
  30. if 'dockerfile' in service:
  31. service['build'] = {
  32. 'context': service.pop('build'),
  33. 'dockerfile': service.pop('dockerfile'),
  34. }
  35. # create logging section
  36. if 'log_driver' in service:
  37. service['logging'] = {'driver': service.pop('log_driver')}
  38. if 'log_opt' in service:
  39. service['logging']['options'] = service.pop('log_opt')
  40. # volumes_from prefix with 'container:'
  41. for idx, volume_from in enumerate(service.get('volumes_from', [])):
  42. if volume_from.split(':', 1)[0] not in service_names:
  43. service['volumes_from'][idx] = 'container:%s' % volume_from
  44. data['services'] = {name: data.pop(name) for name in data.keys()}
  45. data['version'] = 2
  46. return data
  47. def write(stream, new_format, indent, width):
  48. ruamel.yaml.dump(
  49. new_format,
  50. stream,
  51. Dumper=ruamel.yaml.RoundTripDumper,
  52. indent=indent,
  53. width=width)
  54. def parse_opts(args):
  55. parser = argparse.ArgumentParser()
  56. parser.add_argument("filename", help="Compose file filename.")
  57. parser.add_argument("-i", "--in-place", action='store_true')
  58. parser.add_argument(
  59. "--indent", type=int, default=2,
  60. help="Number of spaces used to indent the output yaml.")
  61. parser.add_argument(
  62. "--width", type=int, default=80,
  63. help="Number of spaces used as the output width.")
  64. return parser.parse_args()
  65. def main(args):
  66. logging.basicConfig()
  67. opts = parse_opts(args)
  68. with open(opts.filename, 'r') as fh:
  69. new_format = migrate(fh.read())
  70. if opts.in_place:
  71. output = open(opts.filename, 'w')
  72. else:
  73. output = sys.stdout
  74. write(output, new_format, opts.indent, opts.width)
  75. if __name__ == "__main__":
  76. main(sys.argv)