Merge pull request #2754 from aanand/upgrading-improvements

Improvements to upgrade guide and script
This commit is contained in:
Aanand Prasad 2016-01-27 13:48:23 +00:00
commit 8c250e220c
2 changed files with 127 additions and 32 deletions

View File

@ -12,6 +12,8 @@ import sys
import ruamel.yaml
from compose.config.types import VolumeSpec
log = logging.getLogger('migrate')
@ -20,44 +22,116 @@ 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))
warn_for_links(name, service)
warn_for_external_links(name, service)
rewrite_net(service, service_names)
rewrite_build(service)
rewrite_logging(service)
rewrite_volumes_from(service, service_names)
# net is now networks
if 'net' in service:
service['networks'] = [service.pop('net')]
services = {name: data.pop(name) for name in data.keys()}
# 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
data['services'] = services
create_volumes_section(data)
return data
def warn_for_links(name, service):
links = service.get('links')
if links:
example_service = links[0].partition(':')[0]
log.warn(
"Service {name} has links, which no longer create environment "
"variables such as {example_service_upper}_PORT. "
"If you are using those in your application code, you should "
"instead connect directly to the hostname, e.g. "
"'{example_service}'."
.format(name=name, example_service=example_service,
example_service_upper=example_service.upper()))
def warn_for_external_links(name, service):
external_links = service.get('external_links')
if external_links:
log.warn(
"Service {name} has external_links: {ext}, which now work "
"slightly differently. In particular, two containers must be "
"connected to at least one network in common in order to "
"communicate, even if explicitly linked together.\n\n"
"Either connect the external container to your app's default "
"network, or connect both the external container and your "
"service's containers to a pre-existing network. See "
"https://docs.docker.com/compose/networking/ "
"for more on how to do this."
.format(name=name, ext=external_links))
def rewrite_net(service, service_names):
if 'net' in service:
network_mode = service.pop('net')
# "container:<service name>" is now "service:<service name>"
if network_mode.startswith('container:'):
name = network_mode.partition(':')[2]
if name in service_names:
network_mode = 'service:{}'.format(name)
service['network_mode'] = network_mode
def rewrite_build(service):
if 'dockerfile' in service:
service['build'] = {
'context': service.pop('build'),
'dockerfile': service.pop('dockerfile'),
}
def rewrite_logging(service):
if 'log_driver' in service:
service['logging'] = {'driver': service.pop('log_driver')}
if 'log_opt' in service:
service['logging']['options'] = service.pop('log_opt')
def rewrite_volumes_from(service, service_names):
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
def create_volumes_section(data):
named_volumes = get_named_volumes(data['services'])
if named_volumes:
log.warn(
"Named volumes ({names}) must be explicitly declared. Creating a "
"'volumes' section with declarations.\n\n"
"For backwards-compatibility, they've been declared as external. "
"If you don't mind the volume names being prefixed with the "
"project name, you can remove the 'external' option from each one."
.format(names=', '.join(list(named_volumes))))
data['volumes'] = named_volumes
def get_named_volumes(services):
volume_specs = [
VolumeSpec.parse(volume)
for service in services.values()
for volume in service.get('volumes', [])
]
names = {
spec.external
for spec in volume_specs
if spec.is_named_volume
}
return {name: {'external': True} for name in names}
def write(stream, new_format, indent, width):
ruamel.yaml.dump(
new_format,
@ -81,7 +155,7 @@ def parse_opts(args):
def main(args):
logging.basicConfig()
logging.basicConfig(format='\033[33m%(levelname)s:\033[37m %(message)s\n')
opts = parse_opts(args)

View File

@ -941,6 +941,27 @@ It's more complicated if you're using particular configuration features:
net: "container:cont-name" -> network_mode: "container:cont-name"
net: "container:abc12345" -> network_mode: "container:abc12345"
- `volumes` with named volumes: these must now be explicitly declared in a
top-level `volumes` section of your Compose file. If a service mounts a
named volume called `data`, you must declare a `data` volume in your
top-level `volumes` section. The whole file might look like this:
version: 2
services:
db:
image: postgres
volumes:
- data:/var/lib/postgresql/data
volumes:
data: {}
By default, Compose creates a volume whose name is prefixed with your
project name. If you want it to just be called `data`, declared it as
external:
volumes:
data:
external: true
## Variable substitution