Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use labels instead of names to identify containers #1356

Merged
merged 3 commits into from
May 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -483,6 +485,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
Loading