Skip to content

Commit

Permalink
Merge pull request docker#1356 from dnephin/use_labels_instead_of_names
Browse files Browse the repository at this point in the history
Use labels instead of names to identify containers
  • Loading branch information
aanand committed May 18, 2015
2 parents 4ef3bbc + 62059d5 commit 1e6d912
Show file tree
Hide file tree
Showing 13 changed files with 283 additions and 123 deletions.
1 change: 0 additions & 1 deletion compose/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from __future__ import unicode_literals
from .service import Service # noqa:flake8

__version__ = '1.3.0dev'
33 changes: 19 additions & 14 deletions compose/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import dockerpty

from .. import __version__
from .. import migration
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..config import parse_environment
Expand Down Expand Up @@ -81,20 +82,21 @@ class TopLevelCommand(Command):
-v, --version Print version and exit
Commands:
build Build or rebuild services
help Get help on a command
kill Kill containers
logs View output from containers
port Print the public port for a port binding
ps List containers
pull Pulls service images
restart Restart services
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
up Create and start containers
build Build or rebuild services
help Get help on a command
kill Kill containers
logs View output from containers
port Print the public port for a port binding
ps List containers
pull Pulls service images
restart Restart services
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
up Create and start containers
migrate_to_labels Recreate containers to add labels
"""
def docopt_options(self):
Expand Down Expand Up @@ -482,6 +484,9 @@ def handler(signal, frame):
params = {} if timeout is None else {'timeout': int(timeout)}
project.stop(service_names=service_names, **params)

def migrate_to_labels(self, project, _options):
migration.migrate_project_to_labels(project)


def list_containers(containers):
return ", ".join(c.name for c in containers)
6 changes: 6 additions & 0 deletions compose/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

LABEL_CONTAINER_NUMBER = 'com.docker.compose.container-number'
LABEL_ONE_OFF = 'com.docker.compose.oneoff'
LABEL_PROJECT = 'com.docker.compose.project'
LABEL_SERVICE = 'com.docker.compose.service'
LABEL_VERSION = 'com.docker.compose.version'
14 changes: 9 additions & 5 deletions compose/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import six
from functools import reduce

from .const import LABEL_CONTAINER_NUMBER, LABEL_SERVICE


class Container(object):
"""
Expand Down Expand Up @@ -58,14 +60,15 @@ def name(self):

@property
def name_without_project(self):
return '_'.join(self.dictionary['Name'].split('_')[1:])
return '{0}_{1}'.format(self.labels.get(LABEL_SERVICE), self.number)

@property
def number(self):
try:
return int(self.name.split('_')[-1])
except ValueError:
return None
number = self.labels.get(LABEL_CONTAINER_NUMBER)
if not number:
raise ValueError("Container {0} does not have a {1} label".format(
self.short_id, LABEL_CONTAINER_NUMBER))
return int(number)

@property
def ports(self):
Expand Down Expand Up @@ -159,6 +162,7 @@ def inspect(self):
self.has_been_inspected = True
return self.dictionary

# TODO: only used by tests, move to test module
def links(self):
links = []
for container in self.client.containers():
Expand Down
35 changes: 35 additions & 0 deletions compose/migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import logging
import re

from .container import get_container_name, Container


log = logging.getLogger(__name__)


# TODO: remove this section when migrate_project_to_labels is removed
NAME_RE = re.compile(r'^([^_]+)_([^_]+)_(run_)?(\d+)$')


def is_valid_name(name):
match = NAME_RE.match(name)
return match is not None


def add_labels(project, container, name):
project_name, service_name, one_off, number = NAME_RE.match(name).groups()
if project_name != project.name or service_name not in project.service_names:
return
service = project.get_service(service_name)
service.recreate_container(container)


def migrate_project_to_labels(project):
log.info("Running migration to labels for project %s", project.name)

client = project.client
for container in client.containers(all=True):
name = get_container_name(container)
if not is_valid_name(name):
continue
add_labels(project, Container.from_ps(client, container), name)
39 changes: 31 additions & 8 deletions compose/project.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from __future__ import unicode_literals
from __future__ import absolute_import
import logging

from functools import reduce

from docker.errors import APIError

from .config import get_service_name_from_net, ConfigurationError
from .service import Service
from .const import LABEL_PROJECT, LABEL_ONE_OFF
from .service import Service, check_for_legacy_containers
from .container import Container
from docker.errors import APIError

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -60,6 +62,12 @@ def __init__(self, name, services, client):
self.services = services
self.client = client

def labels(self, one_off=False):
return [
'{0}={1}'.format(LABEL_PROJECT, self.name),
'{0}={1}'.format(LABEL_ONE_OFF, "True" if one_off else "False"),
]

@classmethod
def from_dicts(cls, name, service_dicts, client):
"""
Expand All @@ -75,6 +83,10 @@ def from_dicts(cls, name, service_dicts, client):
volumes_from=volumes_from, **service_dict))
return project

@property
def service_names(self):
return [service.name for service in self.services]

def get_service(self, name):
"""
Retrieve a service by name. Raises NoSuchService
Expand Down Expand Up @@ -102,7 +114,7 @@ def get_services(self, service_names=None, include_deps=False):
"""
if service_names is None or len(service_names) == 0:
return self.get_services(
service_names=[s.name for s in self.services],
service_names=self.service_names,
include_deps=include_deps
)
else:
Expand Down Expand Up @@ -223,10 +235,21 @@ def remove_stopped(self, service_names=None, **options):
service.remove_stopped(**options)

def containers(self, service_names=None, stopped=False, one_off=False):
return [Container.from_ps(self.client, container)
for container in self.client.containers(all=stopped)
for service in self.get_services(service_names)
if service.has_container(container, one_off=one_off)]
containers = [
Container.from_ps(self.client, container)
for container in self.client.containers(
all=stopped,
filters={'label': self.labels(one_off=one_off)})]

if not containers:
check_for_legacy_containers(
self.client,
self.name,
self.service_names,
stopped=stopped,
one_off=one_off)

return containers

def _inject_deps(self, acc, service):
net_name = service.get_net_name()
Expand Down

0 comments on commit 1e6d912

Please sign in to comment.