Skip to content
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
17 changes: 11 additions & 6 deletions compose/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from .. import __version__
from .. import migration
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..service import BuildError, CannotBeScaledError, NeedsBuildError
from ..config import parse_environment
from .command import Command
from .docopt_command import NoSuchCommand
Expand Down Expand Up @@ -47,6 +47,9 @@ def main():
except BuildError as e:
log.error("Service '%s' failed to build: %s" % (e.service.name, e.reason))
sys.exit(1)
except NeedsBuildError as e:
log.error("Service '%s' needs to be built, but --no-build was passed." % e.service.name)
sys.exit(1)


def setup_logging():
Expand Down Expand Up @@ -297,9 +300,8 @@ def run(self, project, options):
project.up(
service_names=deps,
start_deps=True,
recreate=False,
allow_recreate=False,
insecure_registry=insecure_registry,
detach=options['-d']
)

tty = True
Expand Down Expand Up @@ -441,6 +443,8 @@ def up(self, project, options):
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--x-smart-recreate Only recreate containers whose configuration or
image needs to be updated. (EXPERIMENTAL)
--no-recreate If containers already exist, don't recreate them.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
Expand All @@ -453,15 +457,16 @@ def up(self, project, options):
monochrome = options['--no-color']

start_deps = not options['--no-deps']
recreate = not options['--no-recreate']
allow_recreate = not options['--no-recreate']
smart_recreate = options['--x-smart-recreate']
service_names = options['SERVICE']

project.up(
service_names=service_names,
start_deps=start_deps,
recreate=recreate,
allow_recreate=allow_recreate,
smart_recreate=smart_recreate,
insecure_registry=insecure_registry,
detach=detached,
do_build=not options['--no-build'],
)

Expand Down
1 change: 1 addition & 0 deletions compose/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
LABEL_PROJECT = 'com.docker.compose.project'
LABEL_SERVICE = 'com.docker.compose.service'
LABEL_VERSION = 'com.docker.compose.version'
LABEL_CONFIG_HASH = 'com.docker.compose.config-hash'
5 changes: 4 additions & 1 deletion compose/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,16 @@ def attach_socket(self, **kwargs):
return self.client.attach_socket(self.id, **kwargs)

def __repr__(self):
return '<Container: %s>' % self.name
return '<Container: %s (%s)>' % (self.name, self.id[:6])

def __eq__(self, other):
if type(self) != type(other):
return False
return self.id == other.id

def __hash__(self):
return self.id.__hash__()


def get_container_name(container):
if not container.get('Name') and not container.get('Names'):
Expand Down
66 changes: 49 additions & 17 deletions compose/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,24 +207,59 @@ def build(self, service_names=None, no_cache=False):
def up(self,
service_names=None,
start_deps=True,
recreate=True,
allow_recreate=True,
smart_recreate=False,
insecure_registry=False,
detach=False,
do_build=True):
running_containers = []
for service in self.get_services(service_names, include_deps=start_deps):
if recreate:
create_func = service.recreate_containers

services = self.get_services(service_names, include_deps=start_deps)

plans = self._get_convergence_plans(
services,
allow_recreate=allow_recreate,
smart_recreate=smart_recreate,
)

return [
container
for service in services
for container in service.execute_convergence_plan(
plans[service.name],
insecure_registry=insecure_registry,
do_build=do_build,
)
]

def _get_convergence_plans(self,
services,
allow_recreate=True,
smart_recreate=False):

plans = {}

for service in services:
updated_dependencies = [
name
for name in service.get_dependency_names()
if name in plans
and plans[name].action == 'recreate'
]

if updated_dependencies:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we were chatting about #1395 I suggested to support the feature we'd probably want to store the DAG of the services in compose.project.sort_service_dicts() instead of flattening it immediately.

I think that data structure would make this easier as well. Instead of having to lookup the dependencies for each service, we could immediately add recreate plans for all dependencies. When it's available we could consider using it here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think that'd be a good refactor as part of implementing #1395.

log.debug(
'%s has not changed but its dependencies (%s) have, so recreating',
service.name, ", ".join(updated_dependencies),
)
plan = service.recreate_plan()
else:
create_func = service.start_or_create_containers
plan = service.convergence_plan(
allow_recreate=allow_recreate,
smart_recreate=smart_recreate,
)

for container in create_func(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
running_containers.append(container)
plans[service.name] = plan

return running_containers
return plans

def pull(self, service_names=None, insecure_registry=False):
for service in self.get_services(service_names, include_deps=True):
Expand Down Expand Up @@ -252,10 +287,7 @@ def containers(self, service_names=None, stopped=False, one_off=False):
return containers

def _inject_deps(self, acc, service):
net_name = service.get_net_name()
dep_names = (service.get_linked_names() +
service.get_volumes_from_names() +
([net_name] if net_name else []))
dep_names = service.get_dependency_names()

if len(dep_names) > 0:
dep_services = self.get_services(
Expand Down
Loading