|
@@ -0,0 +1,99 @@
|
|
|
+#!/usr/bin/env python
|
|
|
+"""
|
|
|
+Migrate a Compose file from the V1 format in Compose 1.5 to the V2 format
|
|
|
+supported by Compose 1.6+
|
|
|
+"""
|
|
|
+from __future__ import absolute_import
|
|
|
+from __future__ import unicode_literals
|
|
|
+
|
|
|
+import argparse
|
|
|
+import logging
|
|
|
+import sys
|
|
|
+
|
|
|
+import ruamel.yaml
|
|
|
+
|
|
|
+
|
|
|
+log = logging.getLogger('migrate')
|
|
|
+
|
|
|
+
|
|
|
+def migrate(content):
|
|
|
+ data = ruamel.yaml.load(content, ruamel.yaml.RoundTripLoader)
|
|
|
+
|
|
|
+ service_names = data.keys()
|
|
|
+ for name, service in data.items():
|
|
|
+ # remove links and external links
|
|
|
+ service.pop('links', None)
|
|
|
+ external_links = service.pop('external_links', None)
|
|
|
+ if external_links:
|
|
|
+ log.warn(
|
|
|
+ "Service {name} has external_links: {ext}, which are no longer "
|
|
|
+ "supported. See https://docs.docker.com/compose/networking/ "
|
|
|
+ "for options on how to connect external containers to the "
|
|
|
+ "compose network.".format(name=name, ext=external_links))
|
|
|
+
|
|
|
+ # net is now networks
|
|
|
+ if 'net' in service:
|
|
|
+ service['networks'] = [service.pop('net')]
|
|
|
+
|
|
|
+ # create build section
|
|
|
+ if 'dockerfile' in service:
|
|
|
+ service['build'] = {
|
|
|
+ 'context': service.pop('build'),
|
|
|
+ 'dockerfile': service.pop('dockerfile'),
|
|
|
+ }
|
|
|
+
|
|
|
+ # create logging section
|
|
|
+ if 'log_driver' in service:
|
|
|
+ service['logging'] = {'driver': service.pop('log_driver')}
|
|
|
+ if 'log_opt' in service:
|
|
|
+ service['logging']['options'] = service.pop('log_opt')
|
|
|
+
|
|
|
+ # volumes_from prefix with 'container:'
|
|
|
+ for idx, volume_from in enumerate(service.get('volumes_from', [])):
|
|
|
+ if volume_from.split(':', 1)[0] not in service_names:
|
|
|
+ service['volumes_from'][idx] = 'container:%s' % volume_from
|
|
|
+
|
|
|
+ data['services'] = {name: data.pop(name) for name in data.keys()}
|
|
|
+ data['version'] = 2
|
|
|
+ return data
|
|
|
+
|
|
|
+
|
|
|
+def write(stream, new_format, indent, width):
|
|
|
+ ruamel.yaml.dump(
|
|
|
+ new_format,
|
|
|
+ stream,
|
|
|
+ Dumper=ruamel.yaml.RoundTripDumper,
|
|
|
+ indent=indent,
|
|
|
+ width=width)
|
|
|
+
|
|
|
+
|
|
|
+def parse_opts(args):
|
|
|
+ parser = argparse.ArgumentParser()
|
|
|
+ parser.add_argument("filename", help="Compose file filename.")
|
|
|
+ parser.add_argument("-i", "--in-place", action='store_true')
|
|
|
+ parser.add_argument(
|
|
|
+ "--indent", type=int, default=2,
|
|
|
+ help="Number of spaces used to indent the output yaml.")
|
|
|
+ parser.add_argument(
|
|
|
+ "--width", type=int, default=80,
|
|
|
+ help="Number of spaces used as the output width.")
|
|
|
+ return parser.parse_args()
|
|
|
+
|
|
|
+
|
|
|
+def main(args):
|
|
|
+ logging.basicConfig()
|
|
|
+
|
|
|
+ opts = parse_opts(args)
|
|
|
+
|
|
|
+ with open(opts.filename, 'r') as fh:
|
|
|
+ new_format = migrate(fh.read())
|
|
|
+
|
|
|
+ if opts.in_place:
|
|
|
+ output = open(opts.filename, 'w')
|
|
|
+ else:
|
|
|
+ output = sys.stdout
|
|
|
+ write(output, new_format, opts.indent, opts.width)
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main(sys.argv)
|