From 95c1ed9588ad419c71714873cb3c3eccbdb92849 Mon Sep 17 00:00:00 2001 From: Gil Clark Date: Sun, 30 Nov 2014 12:12:49 -0800 Subject: [PATCH 1/3] Treat volumes_from, net containers, and links add dependencies for service ordering. Added depends_on to allow arbitrary dependency specification. Signed-off-by: Gil Clark --- fig.sublime-project | 8 + fig.sublime-workspace | 674 ++++++++++++++++++++++++++++++ fig/__init__.py | 2 +- fig/cli/main.py | 6 +- fig/project.py | 107 ++++- fig/service.py | 42 +- tests/integration/project_test.py | 255 ++++++++++- tests/unit/project_test.py | 158 ++++++- tests/unit/service_test.py | 20 +- tests/unit/sort_service_test.py | 97 ++++- 10 files changed, 1296 insertions(+), 73 deletions(-) create mode 100644 fig.sublime-project create mode 100644 fig.sublime-workspace diff --git a/fig.sublime-project b/fig.sublime-project new file mode 100644 index 00000000000..03077978d15 --- /dev/null +++ b/fig.sublime-project @@ -0,0 +1,8 @@ +{ + "folders": + [ + { + "path": "/Users/gwclark/Git/fig" + } + ] +} diff --git a/fig.sublime-workspace b/fig.sublime-workspace new file mode 100644 index 00000000000..760fa83b8b4 --- /dev/null +++ b/fig.sublime-workspace @@ -0,0 +1,674 @@ +{ + "auto_complete": + { + "selected_items": + [ + [ + "ser", + "service_name" + ], + [ + "fig", + "figtest_net_container" + ], + [ + "isn", + "isinstance" + ], + [ + "con", + "containers" + ], + [ + "mock", + "mock_client" + ], + [ + "dep", + "dependency" + ], + [ + "container", + "container_name" + ], + [ + "conta", + "container_name" + ], + [ + "from", + "from_ps" + ], + [ + "cont", + "container_name" + ], + [ + "volum", + "volume_name" + ], + [ + "__", + "__future__" + ], + [ + "moc", + "mock_client" + ], + [ + "serv", + "service_dict" + ], + [ + "sec", + "service_dict" + ], + [ + "get_r", + "get_service" + ], + [ + "quo", + "qbouipod" + ], + [ + "volume", + "volumes_from" + ], + [ + "curre", + "current_service" + ], + [ + "link", + "link_name" + ], + [ + "get", + "get_service" + ], + [ + "depe", + "dependency" + ], + [ + "servic", + "services" + ], + [ + "temp", + "temp_service_list" + ], + [ + "service", + "service_dict" + ], + [ + "sr", + "services" + ], + [ + "sor", + "sorted_services" + ], + [ + "qbo", + "qbouivol" + ], + [ + "No", + "NoSuchService" + ], + [ + "servi", + "service" + ], + [ + "de", + "dependencies" + ], + [ + "Con", + "ConfigurationError" + ], + [ + "dpe", + "dependency_name" + ], + [ + "dp", + "dependency" + ], + [ + "is", + "isinstance" + ], + [ + "deps", + "dependencies" + ], + [ + "ge", + "get_dependencies" + ], + [ + "_in", + "_inject_deps" + ], + [ + "depen", + "dependencies" + ], + [ + "star", + "start_deps" + ], + [ + "include", + "include_deps" + ], + [ + "pro", + "project" + ], + [ + "F", + "Fprintln n int, err error ƒ" + ], + [ + "res", + "rescue" + ], + [ + "auth", + "auth_pw" + ], + [ + "strip", + "stripnamespace" + ], + [ + "stri", + "stripnamespace" + ], + [ + "st", + "stripnamespace" + ], + [ + "re", + "record" + ], + [ + "line", + "linerecords" + ], + [ + "elem", + "elem_to_internal" + ], + [ + "opt", + "options" + ], + [ + "comm", + "command_data" + ], + [ + "pu", + "publish_job" + ], + [ + "job", + "job_details" + ], + [ + "def", + "defs Method" + ] + ] + }, + "buffers": + [ + ], + "build_system": "", + "command_palette": + { + "height": 392.0, + "selected_items": + [ + [ + ":w", + ":w - Save" + ], + [ + ":1", + "Snippet: Array.new(10) { |i| .. }" + ], + [ + "Packa", + "Package Control: Install Package" + ], + [ + ":$", + ":$ - EOF" + ], + [ + ":q", + "Set Syntax: SQL" + ], + [ + ":4", + "Set Syntax: camlp4" + ], + [ + "", + "Set Syntax: TeX" + ], + [ + "Snippet: ", + "Snippet: #!/usr/bin/env" + ] + ], + "width": 449.0 + }, + "console": + { + "height": 125.0 + }, + "distraction_free": + { + "menu_visible": true, + "show_minimap": false, + "show_open_files": false, + "show_tabs": false, + "side_bar_visible": false, + "status_bar_visible": false + }, + "file_history": + [ + "/Users/gwclark/Git/figdeps/fig/project.py", + "/Users/gwclark/Git/figdeps/fig/service.py", + "/Users/gwclark/Git/figdeps/tests/integration/service_test.py", + "/Users/gwclark/Git/figdeps/tests/integration/project_test.py", + "/Users/gwclark/Git/figdeps/fig/container.py", + "/Users/gwclark/Git/figdeps/fig/cli/docker_client.py", + "/Users/gwclark/Git/figdeps/fig/cli/command.py", + "/Users/gwclark/Git/figdeps/tests/integration/testcases.py", + "/Users/gwclark/Git/figdeps/tests/unit/service_test.py", + "/Users/gwclark/Git/figdeps/tests/unit/project_test.py", + "/Users/gwclark/Git/figdeps/tests/unit/sort_service_test.py", + "/Users/gwclark/Git/figdeps/fig/untitled", + "/Users/gwclark/Git/figdeps/fig/cli/main.py", + "/Users/gwclark/Git/figdeps/fig.yml", + "/Users/gwclark/Git/fig.yml", + "/Users/gwclark/Git/fig/fig/container.py", + "/Users/gwclark/Git/fig/fig/service.py", + "/Users/gwclark/Downloads/cf.json", + "/Users/gwclark/Git/fig/fig/cli/main.py", + "/Users/gwclark/Git/fig/fig/project.py", + "/Users/gwclark/Git/fig/fig/cli/docker_client.py", + "/Users/gwclark/Git/fig/fig/cli/__init__.py", + "/Users/gwclark/Git/fig/fig/cli/colors.py", + "/Users/gwclark/Vagrant/JenkinsWF/Vagrantfile", + "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/User/Preferences.sublime-settings", + "/Users/gwclark/Desktop/foo.go", + "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/GoSublime/CHANGELOG.md", + "/Users/gwclark/Vagrant/Go/Vagrantfile", + "/Users/gwclark/Downloads/lab_files/refappconf/cookbooks/apache2/templates/default/web_app.conf.erb", + "/Users/gwclark/Downloads/lab_files/refappconf/cookbooks/apache2/templates/default/ports.conf.erb", + "/Users/gwclark/Downloads/lab_files/refappconf/cookbooks/apache2/templates/default/mods/proxy.conf.erb", + "/Users/gwclark/Downloads/lab_files/Async.json", + "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/frontend-controller-c1.json", + "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/php-redis/Dockerfile", + "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/frontend-service.json", + "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/frontend-controller-c2.json", + "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/php-redis/index.php", + "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/php-redis/controllers.js", + "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/redis-master.json", + "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/redis-master-service.json", + "/Users/gwclark/Vagrant/Centos/chef-repo/cookbooks/apache/attributes/default.rb", + "/Users/gwclark/Vagrant/Centos/chef-repo/cookbooks/apache/templates/default/index.html.erb", + "/Users/gwclark/Vagrant/Centos/chef-repo/cookbooks/apache/templates/default/custom.erb", + "/Users/gwclark/Vagrant/Centos/chef-repo/cookbooks/apache/recipes/default.rb", + "/Users/gwclark/Vagrant/Centos/chef-repo/cookbooks/apache/templates/default/index.html.erb.html", + "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/Default/Default (OSX).sublime-keymap", + "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/Default/Preferences.sublime-settings", + "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/User/Default (OSX).sublime-keymap", + "/Users/gwclark/Git/swagger/swagger-ui/README.md", + "/Users/gwclark/Downloads/jquery-ui-1.10.4/jquery-1.10.2.js", + "/Users/gwclark/Downloads/jQuery-Plugin-For-Drag-Drop-Multi-Select-List-Box-fieldChooser/README.md", + "/Users/gwclark/Downloads/jQuery-Plugin-For-Drag-Drop-Multi-Select-List-Box-fieldChooser/fieldChooser.jquery.json", + "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_perforce_2000/IMS/working/RDG/Processing/Dependency_Map/Drawing#1.pde", + "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_perforce_2000/IMS/working/RDG/Processing/Dependency_Map/App_Server#1.pde", + "/Users/gwclark/Git/hydra/nodejs_server/app1.bom", + "/Users/gwclark/Git/hydra/nodejs_server/ihub_database.service", + "/Users/gwclark/Git/hydra/nodejs_server/app1.service", + "/Users/gwclark/Git/hydra/nodejs_server/service1.service", + "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_perforce_2000/Quickbooks/Main/Source/QB/Platform/Online/Chaos/_qbchaos#1.def", + "/Users/gwclark/Git/cto/common-library/pom.xml", + "/private/tmp/gwclark(p4v)/hlhdsmscap01.corp.intuit.net_scm.payments.intuit.com_1666/ihub/dev/ihub-dev1/puppet/overlays/web/workers.properties#9.erb", + "/Users/gwclark/Downloads/Magic.tcx", + "/Users/gwclark/Magic.xml", + "/Users/gwclark/Felton Loop.tcx", + "/Users/gwclark/Magic.tcx", + "/Users/gwclark/Downloads/examples/examples/01.01.loancalc.html", + "/Users/gwclark/foo.html", + "/Users/gwclark/Downloads/whp-tomcat-7/conf/catalina.properties", + "/app/jenkins3/workspace/transaction-trunk-qa/pom.xml", + "/app/jenkins3/workspace/transaction-trunk-qa/pom", + "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_perforce_2000/IMS/transaction/build/src/main/script/setConfigRepoDirs#1.groovy", + "/Volumes/GARMIN/Garmin/Settings/Settings.fit", + "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_scm.payments.intuit.com_1666/ihub/dev/ihub-dev1/puppet/overlays/app_tomcat2/mytomcat#1.env", + "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_scm.payments.intuit.com_1666/ihub/dev/ihub-dev1/puppet/overlays/app_cdm/cdmUsers#2.properties", + "/Users/gwclark/Perforce/PSD/DevOps/Monitoring/Jamoon/SNMP-Connector/v3-multiple-users.py", + "/Users/gwclark/bin/MuleStudio/plugins/org.mule.tooling.ui.contribution.esper_1.0.0.201307051648/plugin.xml", + "/Users/gwclark/jamoon/pom.xml", + "/Users/gwclark/Git/facs/message.txt", + "/Users/gwclark/Vagrant/Centos/Vagrantfile", + "/Users/gwclark/.ssh/id_rsa.pub", + "/Users/gwclark/maven_setup/shit.py", + "/Users/gwclark/maven_setup/sbd-buildserver-settings.xml", + "/Volumes/gwclark/Library/Scripts/foo", + "/Users/gwclark/Git/salt/salt/loader.py", + "/Users/gwclark/centra.wsdl", + "/Users/gwclark/savon.rb", + "/Users/gwclark/Documents/seekat.process", + "/Users/gwclark/Library/Caches/TemporaryItems/Outlook Temp/props.conf", + "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/User/Plain text.sublime-settings", + "/Users/gwclark/Vagrant/Centos/ihubtojson.py", + "/Users/gwclark/Vagrant/Centos/samplewonewlines.txt", + "/Users/gwclark/Vagrant/Centos/shit", + "/Users/gwclark/Vagrant/Centos/samplealljson.txt", + "/Users/gwclark/Vagrant/Centos/fuckk", + "/Users/gwclark/Vagrant/Centos/fuck.json", + "/Users/gwclark/Vagrant/Centos/fuck", + "/Users/gwclark/Vagrant/Centos/sample.mmcdmServiceRequestAuditJSON.log.json", + "/Users/gwclark/Vagrant/Centos/sample.mmcdmServiceRequestAuditJSON.log.txt", + "/Users/gwclark/Vagrant/Centos/samplewoxml.txt", + "/Users/gwclark/Desktop/Pupp-Chef Notes", + "/Users/gwclark/.m2/settings.xml", + "/Users/gwclark/Git/yum-test/pom.xml", + "/Users/gwclark/.m2/local-settings.xml", + "/Users/gwclark/.m2/psd-dev-settings.xml", + "/Users/gwclark/Vagrant/Centos/salt/minion", + "/Users/gwclark/buildrouter/orchestration_router.py", + "/Library/Python/2.7/site-packages/salt/minion.py", + "/Users/gwclark/buildrouter/job_event.py", + "/Users/gwclark/buildrouter/push_deployment_job.py", + "/Users/gwclark/buildrouter/jenkins_job.py", + "/Users/gwclark/buildrouter/new_node_runner.py", + "/Users/gwclark/buildrouter/run_job.py", + "/Users/gwclark/buildrouter/jenkinsrunner.py", + "/Users/gwclark/buildrouter/job.json", + "/Users/gwclark/Downloads/pyke-1.1.1/examples/knapsack/test.py", + "/Users/gwclark/buildrouter/push_job.py", + "/Users/gwclark/buildrouter/buildrouter.py", + "/Users/gwclark/buildrouter/jenkinsrunner..py", + "/Users/gwclark/buildrouter/client.py", + "/Users/gwclark/server.py", + "/Users/gwclark/client.py", + "/Users/gwclark/foo", + "/Users/gwclark/Git/salt-cloud/saltcloud/clouds/ipc.py", + "/Users/gwclark/Git/salt-cloud/saltcloud/clouds/ec2.py", + "/Users/gwclark/Git/salt-cloud/requirements.txt", + "/Users/gwclark/Git/salt-cloud/setup.py" + ], + "find": + { + "height": 35.0 + }, + "find_in_files": + { + "height": 0.0, + "where_history": + [ + "" + ] + }, + "find_state": + { + "case_sensitive": true, + "find_history": + [ + "ConfigurationError", + "have any configuration", + "have any configuratin", + "doesn't have any", + "isinsta", + "cy_names", + "Container", + "stream", + "log", + "get_dependency_name", + "from_ps", + "one-off", + "one_off", + "get_contain", + ":\n", + "_get_volue", + "get_volumes_from", + "print", + "print_func", + "print", + "print_func", + "_get_net", + "print_func", + "get_services/", + "print", + "get_services", + "get_services/", + "get_services", + "NAME", + "is_valid", + "get_container_name", + "sys", + "get_container_name", + "get_volumes_from", + "_get_volumes_from", + "get_net", + "container:", + "container", + "get_contain", + "get_volume", + "_get_vol", + "Client", + "mock_clie", + "spec=Client", + "links", + "get_volu", + "_get_volu", + "create_ser", + "create_ser//", + "create_ser/", + "create_ser", + "del", + "to_self", + "one-off", + "to_self", + "/", + "to_self", + "kill", + "_get_ne", + "scale", + "start_or_create_con", + "start_or_create_cond", + "_get_l", + "Creating", + "containers", + "get_containers", + "_get_containers", + "_get_net", + "_get_link", + "project =", + "project", + "if client", + "client =", + "NoSuch", + "inject_dep", + "print", + "NoSuch", + "No suc", + "net =", + "get_depe", + "net = ", + "linked", + "False", + "False///////", + "False//////", + "False/////", + "False////", + "False///", + "False//", + "False/", + "False", + "get_volumes_", + "_get_volumes_from", + "start_links", + "has a depende", + "volumes_from", + "Unsupport", + "dependencies", + "visit", + "link", + "get_dependency_names", + "get_depen", + "get_dependency_name", + "_inject", + "/include_dep", + "get_depen", + "link", + "links", + "links//", + "links/", + "links", + "_get_volumes", + "link", + "link_name", + "get_linked", + "link_to", + "links", + "recreate_container", + "volumes", + "volumes_from", + "Unsupported config", + "link", + "link/", + "link", + "get_linked_names", + "link_name", + "line_name", + "link_to_self" + ], + "highlight": true, + "in_selection": false, + "preserve_case": false, + "regex": false, + "replace_history": + [ + "dependant", + "dependency", + "dependencies", + "dependency_name", + "get_dependency", + "dep_to", + "dependencies", + "get_dependency_names", + "dependency_name", + "dep_to_self", + "dependencies" + ], + "reverse": false, + "show_context": true, + "use_buffer2": true, + "whole_word": false, + "wrap": true + }, + "groups": + [ + { + "sheets": + [ + ] + }, + { + "sheets": + [ + ] + } + ], + "incremental_find": + { + "height": 34.0 + }, + "input": + { + "height": 0.0 + }, + "layout": + { + "cells": + [ + [ + 0, + 0, + 1, + 1 + ], + [ + 1, + 0, + 2, + 1 + ] + ], + "cols": + [ + 0.0, + 0.957747186495, + 1.0 + ], + "rows": + [ + 0.0, + 1.0 + ] + }, + "menu_visible": true, + "replace": + { + "height": 64.0 + }, + "save_all_on_build": true, + "select_file": + { + "height": 0.0, + "selected_items": + [ + [ + "", + "/Users/gwclark/Git/salt-cloud/saltcloud/clouds/ipc.py" + ], + [ + "list", + "/Users/gwclark/Git/salt-cloud/saltcloud/clouds/ipc.py" + ] + ], + "width": 0.0 + }, + "select_project": + { + "height": 0.0, + "selected_items": + [ + ], + "width": 0.0 + }, + "show_minimap": true, + "show_open_files": true, + "show_tabs": true, + "side_bar_visible": true, + "side_bar_width": 194.0, + "status_bar_visible": true +} diff --git a/fig/__init__.py b/fig/__init__.py index a7b29e0c909..8f3b0c94e26 100644 --- a/fig/__init__.py +++ b/fig/__init__.py @@ -1,4 +1,4 @@ from __future__ import unicode_literals from .service import Service # noqa:flake8 -__version__ = '1.0.1' +__version__ = '1.0.1-wdeps' diff --git a/fig/cli/main.py b/fig/cli/main.py index e98fea8666e..eabfdd39837 100644 --- a/fig/cli/main.py +++ b/fig/cli/main.py @@ -291,7 +291,7 @@ def run(self, project, options): if len(deps) > 0: project.up( service_names=deps, - start_links=True, + start_deps=True, recreate=False, ) @@ -422,13 +422,13 @@ def up(self, project, options): monochrome = options['--no-color'] - start_links = not options['--no-deps'] + start_deps = not options['--no-deps'] recreate = not options['--no-recreate'] service_names = options['SERVICE'] project.up( service_names=service_names, - start_links=start_links, + start_deps=start_deps, recreate=recreate, insecure_registry=insecure_registry, ) diff --git a/fig/project.py b/fig/project.py index 569df38d580..ad39e152c0e 100644 --- a/fig/project.py +++ b/fig/project.py @@ -9,6 +9,17 @@ log = logging.getLogger(__name__) +def get_service_name_from_net(net_config): + if not net_config: + return + + if not net_config.startswith('container:'): + return + + _, net_name = net_config.split(':', 1) + return net_name + + def sort_service_dicts(services): # Topological sort (Cormen/Tarjan algorithm). unmarked = services[:] @@ -17,6 +28,16 @@ def sort_service_dicts(services): get_service_names = lambda links: [link.split(':')[0] for link in links] + def get_service_dependents(service_dict, services): + name = service_dict['name'] + return [ + service for service in services + if (name in get_service_names(service.get('links', [])) or + name in service.get('volumes_from', []) or + name in service.get('depends_on', []) or + name == get_service_name_from_net(service.get('net'))) + ] + def visit(n): if n['name'] in temporary_marked: if n['name'] in get_service_names(n.get('links', [])): @@ -27,8 +48,7 @@ def visit(n): raise DependencyError('Circular import between %s' % ' and '.join(temporary_marked)) if n in unmarked: temporary_marked.add(n['name']) - dependents = [m for m in services if (n['name'] in get_service_names(m.get('links', []))) or (n['name'] in m.get('volumes_from', []))] - for m in dependents: + for m in get_service_dependents(n, services): visit(m) temporary_marked.remove(n['name']) unmarked.remove(n) @@ -58,8 +78,11 @@ def from_dicts(cls, name, service_dicts, client): for service_dict in sort_service_dicts(service_dicts): links = project.get_links(service_dict) volumes_from = project.get_volumes_from(service_dict) + net = project.get_net(service_dict) + depends_on = project.get_depends_on(service_dict) - project.services.append(Service(client=client, project=name, links=links, volumes_from=volumes_from, **service_dict)) + project.services.append(Service(client=client, project=name, links=links, net=net, volumes_from=volumes_from, + depends_on=depends_on, **service_dict)) return project @classmethod @@ -83,31 +106,31 @@ def get_service(self, name): raise NoSuchService(name) - def get_services(self, service_names=None, include_links=False): + def get_services(self, service_names=None, include_deps=False): """ Returns a list of this project's services filtered by the provided list of names, or all services if service_names is None or []. - If include_links is specified, returns a list including the links for + If include_deps is specified, returns a list including the dependencies for service_names, in order of dependency. Preserves the original order of self.services where possible, - reordering as needed to resolve links. + reordering as needed to resolve dependencies. Raises NoSuchService if any of the named services do not exist. """ if service_names is None or len(service_names) == 0: return self.get_services( service_names=[s.name for s in self.services], - include_links=include_links + include_deps=include_deps ) else: unsorted = [self.get_service(name) for name in service_names] services = [s for s in self.services if s in unsorted] - if include_links: - services = reduce(self._inject_links, services, []) + if include_deps: + services = reduce(self._inject_deps, services, []) uniques = [] [uniques.append(s) for s in services if s not in uniques] @@ -144,6 +167,45 @@ def get_volumes_from(self, service_dict): del service_dict['volumes_from'] return volumes_from + def get_depends_on(self, service_dict): + depends_on = [] + if 'depends_on' in service_dict: + for dep_name in service_dict.get('depends_on', []): + try: + service = self.get_service(dep_name) + depends_on.append(service) + except NoSuchService: + raise ConfigurationError('Service "%s" depends on "%s", which is not the name of a service.' % (service_dict['name'], dep_name)) + del service_dict['depends_on'] + return depends_on + + def get_net(self, service_dict): + if 'net' in service_dict: + net_name = get_service_name_from_net(service_dict.get('net')) + + if net_name: + net = [] + try: + service = self.get_service(net_name) + net.append(service) + del service_dict['net'] + return net + except NoSuchService: + try: + container = Container.from_id(self.client, net_name) + net.append(container) + except APIError: + raise ConfigurationError('Serivce "%s" is trying to use the network of "%s", which is not the name of a service or container.' % (service_dict['name'], net_name)) + else: + net = service_dict['net'] + + del service_dict['net'] + + else: + net = 'bridge' + + return net + def start(self, service_names=None, **options): for service in self.get_services(service_names): service.start(**options) @@ -167,9 +229,9 @@ def build(self, service_names=None, no_cache=False): else: log.info('%s uses an image, skipping' % service.name) - def up(self, service_names=None, start_links=True, recreate=True, insecure_registry=False): + def up(self, service_names=None, start_deps=True, recreate=True, insecure_registry=False): running_containers = [] - for service in self.get_services(service_names, include_links=start_links): + for service in self.get_services(service_names, include_deps=start_deps): if recreate: for (_, container) in service.recreate_containers(insecure_registry=insecure_registry): running_containers.append(container) @@ -180,7 +242,7 @@ def up(self, service_names=None, start_links=True, recreate=True, insecure_regis return running_containers def pull(self, service_names=None, insecure_registry=False): - for service in self.get_services(service_names, include_links=True): + for service in self.get_services(service_names, include_deps=True): service.pull(insecure_registry=insecure_registry) def remove_stopped(self, service_names=None, **options): @@ -193,19 +255,22 @@ def containers(self, service_names=None, stopped=False, one_off=False): for service in self.get_services(service_names) if service.has_container(container, one_off=one_off)] - def _inject_links(self, acc, service): - linked_names = service.get_linked_names() + def _inject_deps(self, acc, service): + dep_names = service.get_linked_names() + \ + service.get_volumes_from_names() + \ + service.get_net_names() + \ + service.get_depends_on_names() - if len(linked_names) > 0: - linked_services = self.get_services( - service_names=linked_names, - include_links=True + if len(dep_names) > 0: + dep_services = self.get_services( + service_names=list(set(dep_names)), + include_deps=True ) else: - linked_services = [] + dep_services = [] - linked_services.append(service) - return acc + linked_services + dep_services.append(service) + return acc + dep_services class NoSuchService(Exception): diff --git a/fig/service.py b/fig/service.py index bbbef7bc43e..67b514579cb 100644 --- a/fig/service.py +++ b/fig/service.py @@ -50,7 +50,7 @@ class ConfigError(ValueError): class Service(object): - def __init__(self, name, client=None, project='default', links=None, volumes_from=None, **options): + def __init__(self, name, client=None, project='default', links=None, volumes_from=None, depends_on=None, net=None, **options): if not re.match('^%s+$' % VALID_NAME_CHARS, name): raise ConfigError('Invalid service name "%s" - only %s are allowed' % (name, VALID_NAME_CHARS)) if not re.match('^%s+$' % VALID_NAME_CHARS, project): @@ -72,6 +72,8 @@ def __init__(self, name, client=None, project='default', links=None, volumes_fro self.project = project self.links = links or [] self.volumes_from = volumes_from or [] + self.depends_on = depends_on or [] + self.net = net or '' self.options = options def containers(self, stopped=False, one_off=False): @@ -259,7 +261,6 @@ def start_container(self, container=None, intermediate_container=None, **overrid if ':' in volume) privileged = options.get('privileged', False) - net = options.get('net', 'bridge') dns = options.get('dns', None) container.start( @@ -268,7 +269,7 @@ def start_container(self, container=None, intermediate_container=None, **overrid binds=volume_bindings, volumes_from=self._get_volumes_from(intermediate_container), privileged=privileged, - network_mode=net, + network_mode=self._get_net(), dns=dns, ) return container @@ -286,6 +287,18 @@ def start_or_create_containers(self, insecure_registry=False): def get_linked_names(self): return [s.name for (s, _) in self.links] + def get_volumes_from_names(self): + return [s.name for s in self.volumes_from if isinstance(s, Service)] + + def get_depends_on_names(self): + return [s.name for s in self.depends_on] + + def get_net_names(self): + if isinstance(self.net, list): + return [s.name for s in self.net if isinstance(s, Service)] + else: + return [] + def _next_container_name(self, all_containers, one_off=False): bits = [self.project, self.name] if one_off: @@ -315,11 +328,7 @@ def _get_volumes_from(self, intermediate_container=None): for volume_source in self.volumes_from: if isinstance(volume_source, Service): containers = volume_source.containers(stopped=True) - - if not containers: - volumes_from.append(volume_source.create_container().id) - else: - volumes_from.extend(map(attrgetter('id'), containers)) + volumes_from.extend(map(attrgetter('id'), containers)) elif isinstance(volume_source, Container): volumes_from.append(volume_source.id) @@ -329,6 +338,23 @@ def _get_volumes_from(self, intermediate_container=None): return volumes_from + def _get_net(self): + net = 'bridge' + if isinstance(self.net, list): + net_source = self.net[0] + if isinstance(net_source, Service): + if len(net_source.containers()) > 0: + container = net_source.containers()[0] + net = 'container:' + container.id + else: + net = 'bridge' + elif isinstance(net_source, Container): + net = 'container:' + net_source.id + else: + net = self.net + + return net + def _get_container_create_options(self, override_options, one_off=False): container_options = dict((k, self.options[k]) for k in DOCKER_CONFIG_KEYS if k in self.options) container_options.update(override_options) diff --git a/tests/integration/project_test.py b/tests/integration/project_test.py index ce087245813..47b156d53bd 100644 --- a/tests/integration/project_test.py +++ b/tests/integration/project_test.py @@ -11,38 +11,104 @@ def test_volumes_from_service(self): config={ 'data': { 'image': 'busybox:latest', - 'volumes': ['/var/data'], + 'volumes': ['/var/data'] }, 'db': { 'image': 'busybox:latest', - 'volumes_from': ['data'], - }, + 'volumes_from': ['data'] + }, }, client=self.client, ) + + project.up() + db = project.get_service('db') data = project.get_service('data') - self.assertEqual(db.volumes_from, [data]) + self.assertEqual(db._get_volumes_from(), [c.id for c in data.containers(stopped=True)]) + + project.kill() + project.remove_stopped() def test_volumes_from_container(self): data_container = Container.create( self.client, image='busybox:latest', volumes=['/var/data'], - name='figtest_data_container', + name='figtest_data_container' ) project = Project.from_config( name='figtest', config={ 'db': { 'image': 'busybox:latest', - 'volumes_from': ['figtest_data_container'], + 'volumes_from': ['figtest_data_container'] }, }, client=self.client, ) + + project.up() + db = project.get_service('db') - self.assertEqual(db.volumes_from, [data_container]) + self.assertEqual(db._get_volumes_from(), [data_container.id]) + + project.kill() + project.remove_stopped() + + def test_net_from_service(self): + project = Project.from_config( + name='figtest', + config={ + 'net': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"] + }, + 'web': { + 'image': 'busybox:latest', + 'net': 'container:net', + 'command': ["/bin/sleep", "300"] + }, + }, + client=self.client, + ) + + project.up() + + web = project.get_service('web') + net = project.get_service('net') + self.assertEqual(web._get_net(), 'container:'+net.containers()[0].id) + + project.kill() + project.remove_stopped() + + def test_net_from_container(self): + net_container = Container.create( + self.client, + image='busybox:latest', + name='figtest_net_container', + command='/bin/sleep 300' + ) + net_container.start() + + project = Project.from_config( + name='figtest', + config={ + 'web': { + 'image': 'busybox:latest', + 'net': 'container:figtest_net_container' + }, + }, + client=self.client, + ) + + project.up() + + web = project.get_service('web') + self.assertEqual(web._get_net(), 'container:'+net_container.id) + + project.kill() + project.remove_stopped() def test_start_stop_kill_remove(self): web = self.create_service('web') @@ -182,37 +248,180 @@ def test_project_up_without_all_services(self): project.remove_stopped() def test_project_up_starts_links(self): - console = self.create_service('console') - db = self.create_service('db', volumes=['/var/db']) - web = self.create_service('web', links=[(db, 'db')]) + project = Project.from_config( + name='figtest', + config={ + 'console': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + }, + 'db': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'volumes': ['/var/db'] + }, + 'web': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'links': ['db:db'] + }, + }, + client=self.client, + ) + project.start() + self.assertEqual(len(project.containers()), 0) + + project.up(['web']) + self.assertEqual(len(project.containers()), 2) + self.assertEqual(len(project.get_service('web').containers()), 1) + self.assertEqual(len(project.get_service('db').containers()), 1) + self.assertEqual(len(project.get_service('console').containers()), 0) + + project.kill() + project.remove_stopped() + + def test_project_up_starts_volumes(self): + project = Project.from_config( + name='figtest', + config={ + 'console': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + }, + 'data': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'volumes': ['/var/db'] + }, + 'db': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'volumes_from': ['data'] + }, + }, + client=self.client, + ) + project.start() + self.assertEqual(len(project.containers()), 0) - project = Project('figtest', [web, db, console], self.client) + project.up(['db']) + self.assertEqual(len(project.containers()), 2) + self.assertEqual(len(project.get_service('db').containers()), 1) + self.assertEqual(len(project.get_service('data').containers()), 1) + self.assertEqual(len(project.get_service('console').containers()), 0) + + project.kill() + project.remove_stopped() + + def test_project_up_starts_net(self): + project = Project.from_config( + name='figtest', + config={ + 'console': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + }, + 'net': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"] + }, + 'web': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'net': 'container:net' + }, + }, + client=self.client, + ) project.start() self.assertEqual(len(project.containers()), 0) project.up(['web']) self.assertEqual(len(project.containers()), 2) - self.assertEqual(len(web.containers()), 1) - self.assertEqual(len(db.containers()), 1) - self.assertEqual(len(console.containers()), 0) + self.assertEqual(len(project.get_service('web').containers()), 1) + self.assertEqual(len(project.get_service('net').containers()), 1) + self.assertEqual(len(project.get_service('console').containers()), 0) project.kill() project.remove_stopped() - def test_project_up_with_no_deps(self): - console = self.create_service('console') - db = self.create_service('db', volumes=['/var/db']) - web = self.create_service('web', links=[(db, 'db')]) + def test_project_up_starts_depends(self): + project = Project.from_config( + name='figtest', + config={ + 'console': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + }, + 'net' : { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"] + }, + 'app': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'net': 'container:net' + }, + 'web': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'net': 'container:net', + 'depends_on': ['app'] + }, + }, + client=self.client, + ) + project.start() + self.assertEqual(len(project.containers()), 0) - project = Project('figtest', [web, db, console], self.client) + project.up(['web']) + self.assertEqual(len(project.containers()), 3) + self.assertEqual(len(project.get_service('web').containers()), 1) + self.assertEqual(len(project.get_service('app').containers()), 1) + self.assertEqual(len(project.get_service('net').containers()), 1) + self.assertEqual(len(project.get_service('console').containers()), 0) + + project.kill() + project.remove_stopped() + + def test_project_up_with_no_deps(self): + project = Project.from_config( + name='figtest', + config={ + 'console': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + }, + 'db': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'volumes': ['/var/db'] + },'web': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'links': ['db:db'] + }, + 'web': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'links': ['db:db'], + 'net': 'container:net' + }, + 'net': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"] + } + }, + client=self.client, + ) project.start() self.assertEqual(len(project.containers()), 0) - project.up(['web'], start_links=False) + project.up(['web'], start_deps=False) self.assertEqual(len(project.containers()), 1) - self.assertEqual(len(web.containers()), 1) - self.assertEqual(len(db.containers()), 0) - self.assertEqual(len(console.containers()), 0) + self.assertEqual(len(project.get_service('web').containers()), 1) + self.assertEqual(len(project.get_service('db').containers()), 0) + self.assertEqual(len(project.get_service('net').containers()), 0) project.kill() project.remove_stopped() diff --git a/tests/unit/project_test.py b/tests/unit/project_test.py index 5c8d35b1d92..0dc8b29b011 100644 --- a/tests/unit/project_test.py +++ b/tests/unit/project_test.py @@ -1,7 +1,11 @@ from __future__ import unicode_literals from .. import unittest from fig.service import Service +from fig.container import Container from fig.project import Project, ConfigurationError +import mock + +import docker class ProjectTest(unittest.TestCase): def test_from_dict(self): @@ -99,7 +103,7 @@ def test_get_services_returns_listed_services_with_args(self): project = Project('test', [web, console], None) self.assertEqual(project.get_services(['console']), [console]) - def test_get_services_with_include_links(self): + def test_get_services_with_include_links_service(self): db = Service( project='figtest', name='db', @@ -120,11 +124,54 @@ def test_get_services_with_include_links(self): ) project = Project('test', [web, db, cache, console], None) self.assertEqual( - project.get_services(['console'], include_links=True), + project.get_services(['console'], include_deps=True), [db, web, console] ) - def test_get_services_removes_duplicates_following_links(self): + def test_get_services_with_include_links_project(self): + project = Project.from_dicts('figtest', [ + { + 'name': 'db', + 'image': 'busybox:latest' + }, + { + 'name': 'web', + 'image': 'busybox:latest', + 'links': ['db:database'], + }, + { + 'name': 'cache', + 'image': 'busybox' + }, + { + 'name': 'console', + 'image': 'busybox:latest', + 'links': ['web:web'] + } + ], None) + self.assertEqual( + [s.name for s in project.get_services(['console'], include_deps=True)], + [s for s in ['db', 'web', 'console']] + ) + + def test_get_services_removes_duplicates_following_links_project(self): + project = Project.from_dicts('test', [ + { + 'name': 'db', + 'image': 'busybox:latest', + }, + { + 'name': 'web', + 'image': 'busybox:latest', + 'links': ['db:database'], + } + ], None) + self.assertEqual( + [s.name for s in project.get_services(['web', 'db'], include_deps=True)], + [s for s in ['db', 'web']] + ) + + def test_get_services_removes_duplicates_following_links_service(self): db = Service( project='figtest', name='db', @@ -136,6 +183,109 @@ def test_get_services_removes_duplicates_following_links(self): ) project = Project('test', [web, db], None) self.assertEqual( - project.get_services(['web', 'db'], include_links=True), + project.get_services(['web', 'db'], include_deps=True), [db, web] ) + + def test_use_volumes_from_container(self): + container_id = 'aabbccddee' + container_dict = dict(Name='aaa', Id=container_id) + mock_client = mock.create_autospec(docker.Client) + mock_client.inspect_container.return_value = container_dict + project = Project.from_dicts('test', [ + { + 'name': 'test', + 'image': 'busybox:latest', + 'volumes_from': ['aaa'] + } + ], mock_client) + + self.assertEqual(project.get_service('test')._get_volumes_from(), [container_id]) + + def test_use_volumes_from_service_no_container(self): + container_name = 'test_vol_1' + mock_client = mock.create_autospec(docker.Client) + mock_client.containers.return_value = [ + { + "Name": container_name, + "Names": [container_name], + "Id": container_name, + "Image": 'busybox:latest' + } + ] + project = Project.from_dicts('test', [ + { + 'name': 'vol', + 'image': 'busybox:latest' + }, + { + 'name': 'test', + 'image': 'busybox:latest', + 'volumes_from': ['vol'] + } + ], mock_client) + + self.assertEqual(project.get_service('test')._get_volumes_from(), [container_name]) + + @mock.patch.object(Service, 'containers') + def test_use_volumes_from_service_container(self, mock_return): + container_ids = ['aabbccddee', '12345'] + mock_return.return_value = [ + mock.Mock(id=container_id, spec=Container) + for container_id in container_ids] + + project = Project.from_dicts('test', [ + { + 'name': 'vol', + 'image': 'busybox:latest' + }, + { + 'name': 'test', + 'image': 'busybox:latest', + 'volumes_from': ['vol'] + } + ], None) + + self.assertEqual(project.get_service('test')._get_volumes_from(), container_ids) + + def test_use_net_from_container(self): + container_id = 'aabbccddee' + container_dict = dict(Name='aaa', Id=container_id) + mock_client = mock.create_autospec(docker.Client) + mock_client.inspect_container.return_value = container_dict + project = Project.from_dicts('test', [ + { + 'name': 'test', + 'image': 'busybox:latest', + 'net': 'container:aaa' + } + ], mock_client) + + service = project.get_service('test') + self.assertEqual(service._get_net(), 'container:'+container_id) + + def test_use_net_from_service(self): + container_name = 'test_aaa_1' + mock_client = mock.create_autospec(docker.Client) + mock_client.containers.return_value = [ + { + "Name": container_name, + "Names": [container_name], + "Id": container_name, + "Image": 'busybox:latest' + } + ] + project = Project.from_dicts('test', [ + { + 'name': 'aaa', + 'image': 'busybox:latest' + }, + { + 'name': 'test', + 'image': 'busybox:latest', + 'net': 'container:aaa' + } + ], mock_client) + + service = project.get_service('test') + self.assertEqual(service._get_net(), 'container:'+container_name) diff --git a/tests/unit/service_test.py b/tests/unit/service_test.py index 119b4144062..ab136d41a44 100644 --- a/tests/unit/service_test.py +++ b/tests/unit/service_test.py @@ -63,6 +63,14 @@ def test_get_volumes_from_intermediate_container(self): self.assertEqual(service._get_volumes_from(container), [container_id]) + def test_get_volumes_from_container(self): + container_id = 'aabbccddee' + service = Service( + 'test', + volumes_from=[mock.Mock(id=container_id, spec=Container)]) + + self.assertEqual(service._get_volumes_from(), [container_id]) + def test_get_volumes_from_service_container_exists(self): container_ids = ['aabbccddee', '12345'] from_service = mock.create_autospec(Service) @@ -74,18 +82,6 @@ def test_get_volumes_from_service_container_exists(self): self.assertEqual(service._get_volumes_from(), container_ids) - def test_get_volumes_from_service_no_container(self): - container_id = 'abababab' - from_service = mock.create_autospec(Service) - from_service.containers.return_value = [] - from_service.create_container.return_value = mock.Mock( - id=container_id, - spec=Container) - service = Service('test', volumes_from=[from_service]) - - self.assertEqual(service._get_volumes_from(), [container_id]) - from_service.create_container.assert_called_once_with() - def test_split_port_with_host_ip(self): internal_port, external_port = split_port("127.0.0.1:1000:2000") self.assertEqual(internal_port, "2000") diff --git a/tests/unit/sort_service_test.py b/tests/unit/sort_service_test.py index e2a7bdb3884..0c1366277e9 100644 --- a/tests/unit/sort_service_test.py +++ b/tests/unit/sort_service_test.py @@ -1,4 +1,4 @@ -from fig.project import sort_service_dicts, DependencyError +from fig.project import sort_service_dicts, DependencyError, Project from .. import unittest @@ -18,6 +18,7 @@ def test_sort_service_dicts_1(self): ] sorted_services = sort_service_dicts(services) + self.assertEqual(len(sorted_services), 3) self.assertEqual(sorted_services[0]['name'], 'grunt') self.assertEqual(sorted_services[1]['name'], 'redis') @@ -65,6 +66,100 @@ def test_sort_service_dicts_3(self): self.assertEqual(sorted_services[1]['name'], 'parent') self.assertEqual(sorted_services[2]['name'], 'grandparent') + def test_sort_service_dicts_4(self): + services = [ + { + 'name': 'child' + }, + { + 'name': 'parent', + 'volumes_from': ['child'] + }, + { + 'links': ['parent'], + 'name': 'grandparent' + }, + ] + + sorted_services = sort_service_dicts(services) + self.assertEqual(len(sorted_services), 3) + self.assertEqual(sorted_services[0]['name'], 'child') + self.assertEqual(sorted_services[1]['name'], 'parent') + self.assertEqual(sorted_services[2]['name'], 'grandparent') + + def test_sort_service_dicts_5(self): + services = [ + { + 'links': ['parent'], + 'name': 'grandparent' + }, + { + 'name': 'parent', + 'net': 'container:child' + }, + { + 'name': 'child' + } + ] + + sorted_services = sort_service_dicts(services) + self.assertEqual(len(sorted_services), 3) + self.assertEqual(sorted_services[0]['name'], 'child') + self.assertEqual(sorted_services[1]['name'], 'parent') + self.assertEqual(sorted_services[2]['name'], 'grandparent') + + def test_sort_service_dicts_6(self): + services = [ + { + 'links': ['parent'], + 'name': 'grandparent' + }, + { + 'name': 'parent', + 'volumes_from': ['child'] + }, + { + 'name': 'child' + } + ] + + sorted_services = sort_service_dicts(services) + self.assertEqual(len(sorted_services), 3) + self.assertEqual(sorted_services[0]['name'], 'child') + self.assertEqual(sorted_services[1]['name'], 'parent') + self.assertEqual(sorted_services[2]['name'], 'grandparent') + + def test_sort_service_dicts_7(self): + services = [ + { + 'depends_on': ['four'], + 'name': 'five' + }, + { + 'net': 'container:three', + 'name': 'four' + }, + { + 'links': ['two'], + 'name': 'three' + }, + { + 'name': 'two', + 'volumes_from': ['one'] + }, + { + 'name': 'one' + } + ] + + sorted_services = sort_service_dicts(services) + self.assertEqual(len(sorted_services), 5) + self.assertEqual(sorted_services[0]['name'], 'one') + self.assertEqual(sorted_services[1]['name'], 'two') + self.assertEqual(sorted_services[2]['name'], 'three') + self.assertEqual(sorted_services[3]['name'], 'four') + self.assertEqual(sorted_services[4]['name'], 'five') + def test_sort_service_dicts_circular_imports(self): services = [ { From ac8d6f5381c28cc40910d7f28b769d1a5a277acc Mon Sep 17 00:00:00 2001 From: Gil Clark Date: Thu, 11 Dec 2014 17:26:18 -0800 Subject: [PATCH 2/3] Incorporated changes suggested by @dnephin. Signed-off-by: Gil Clark --- fig.sublime-project | 8 - fig.sublime-workspace | 674 ------------------------------------- fig/__init__.py | 2 +- fig/project.py | 18 +- fig/service.py | 38 ++- tests/unit/service_test.py | 12 + 6 files changed, 41 insertions(+), 711 deletions(-) delete mode 100644 fig.sublime-project delete mode 100644 fig.sublime-workspace diff --git a/fig.sublime-project b/fig.sublime-project deleted file mode 100644 index 03077978d15..00000000000 --- a/fig.sublime-project +++ /dev/null @@ -1,8 +0,0 @@ -{ - "folders": - [ - { - "path": "/Users/gwclark/Git/fig" - } - ] -} diff --git a/fig.sublime-workspace b/fig.sublime-workspace deleted file mode 100644 index 760fa83b8b4..00000000000 --- a/fig.sublime-workspace +++ /dev/null @@ -1,674 +0,0 @@ -{ - "auto_complete": - { - "selected_items": - [ - [ - "ser", - "service_name" - ], - [ - "fig", - "figtest_net_container" - ], - [ - "isn", - "isinstance" - ], - [ - "con", - "containers" - ], - [ - "mock", - "mock_client" - ], - [ - "dep", - "dependency" - ], - [ - "container", - "container_name" - ], - [ - "conta", - "container_name" - ], - [ - "from", - "from_ps" - ], - [ - "cont", - "container_name" - ], - [ - "volum", - "volume_name" - ], - [ - "__", - "__future__" - ], - [ - "moc", - "mock_client" - ], - [ - "serv", - "service_dict" - ], - [ - "sec", - "service_dict" - ], - [ - "get_r", - "get_service" - ], - [ - "quo", - "qbouipod" - ], - [ - "volume", - "volumes_from" - ], - [ - "curre", - "current_service" - ], - [ - "link", - "link_name" - ], - [ - "get", - "get_service" - ], - [ - "depe", - "dependency" - ], - [ - "servic", - "services" - ], - [ - "temp", - "temp_service_list" - ], - [ - "service", - "service_dict" - ], - [ - "sr", - "services" - ], - [ - "sor", - "sorted_services" - ], - [ - "qbo", - "qbouivol" - ], - [ - "No", - "NoSuchService" - ], - [ - "servi", - "service" - ], - [ - "de", - "dependencies" - ], - [ - "Con", - "ConfigurationError" - ], - [ - "dpe", - "dependency_name" - ], - [ - "dp", - "dependency" - ], - [ - "is", - "isinstance" - ], - [ - "deps", - "dependencies" - ], - [ - "ge", - "get_dependencies" - ], - [ - "_in", - "_inject_deps" - ], - [ - "depen", - "dependencies" - ], - [ - "star", - "start_deps" - ], - [ - "include", - "include_deps" - ], - [ - "pro", - "project" - ], - [ - "F", - "Fprintln n int, err error ƒ" - ], - [ - "res", - "rescue" - ], - [ - "auth", - "auth_pw" - ], - [ - "strip", - "stripnamespace" - ], - [ - "stri", - "stripnamespace" - ], - [ - "st", - "stripnamespace" - ], - [ - "re", - "record" - ], - [ - "line", - "linerecords" - ], - [ - "elem", - "elem_to_internal" - ], - [ - "opt", - "options" - ], - [ - "comm", - "command_data" - ], - [ - "pu", - "publish_job" - ], - [ - "job", - "job_details" - ], - [ - "def", - "defs Method" - ] - ] - }, - "buffers": - [ - ], - "build_system": "", - "command_palette": - { - "height": 392.0, - "selected_items": - [ - [ - ":w", - ":w - Save" - ], - [ - ":1", - "Snippet: Array.new(10) { |i| .. }" - ], - [ - "Packa", - "Package Control: Install Package" - ], - [ - ":$", - ":$ - EOF" - ], - [ - ":q", - "Set Syntax: SQL" - ], - [ - ":4", - "Set Syntax: camlp4" - ], - [ - "", - "Set Syntax: TeX" - ], - [ - "Snippet: ", - "Snippet: #!/usr/bin/env" - ] - ], - "width": 449.0 - }, - "console": - { - "height": 125.0 - }, - "distraction_free": - { - "menu_visible": true, - "show_minimap": false, - "show_open_files": false, - "show_tabs": false, - "side_bar_visible": false, - "status_bar_visible": false - }, - "file_history": - [ - "/Users/gwclark/Git/figdeps/fig/project.py", - "/Users/gwclark/Git/figdeps/fig/service.py", - "/Users/gwclark/Git/figdeps/tests/integration/service_test.py", - "/Users/gwclark/Git/figdeps/tests/integration/project_test.py", - "/Users/gwclark/Git/figdeps/fig/container.py", - "/Users/gwclark/Git/figdeps/fig/cli/docker_client.py", - "/Users/gwclark/Git/figdeps/fig/cli/command.py", - "/Users/gwclark/Git/figdeps/tests/integration/testcases.py", - "/Users/gwclark/Git/figdeps/tests/unit/service_test.py", - "/Users/gwclark/Git/figdeps/tests/unit/project_test.py", - "/Users/gwclark/Git/figdeps/tests/unit/sort_service_test.py", - "/Users/gwclark/Git/figdeps/fig/untitled", - "/Users/gwclark/Git/figdeps/fig/cli/main.py", - "/Users/gwclark/Git/figdeps/fig.yml", - "/Users/gwclark/Git/fig.yml", - "/Users/gwclark/Git/fig/fig/container.py", - "/Users/gwclark/Git/fig/fig/service.py", - "/Users/gwclark/Downloads/cf.json", - "/Users/gwclark/Git/fig/fig/cli/main.py", - "/Users/gwclark/Git/fig/fig/project.py", - "/Users/gwclark/Git/fig/fig/cli/docker_client.py", - "/Users/gwclark/Git/fig/fig/cli/__init__.py", - "/Users/gwclark/Git/fig/fig/cli/colors.py", - "/Users/gwclark/Vagrant/JenkinsWF/Vagrantfile", - "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/User/Preferences.sublime-settings", - "/Users/gwclark/Desktop/foo.go", - "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/GoSublime/CHANGELOG.md", - "/Users/gwclark/Vagrant/Go/Vagrantfile", - "/Users/gwclark/Downloads/lab_files/refappconf/cookbooks/apache2/templates/default/web_app.conf.erb", - "/Users/gwclark/Downloads/lab_files/refappconf/cookbooks/apache2/templates/default/ports.conf.erb", - "/Users/gwclark/Downloads/lab_files/refappconf/cookbooks/apache2/templates/default/mods/proxy.conf.erb", - "/Users/gwclark/Downloads/lab_files/Async.json", - "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/frontend-controller-c1.json", - "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/php-redis/Dockerfile", - "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/frontend-service.json", - "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/frontend-controller-c2.json", - "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/php-redis/index.php", - "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/php-redis/controllers.js", - "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/redis-master.json", - "/Users/gwclark/Git/Kubernetes/kubernetes/examples/guestbook/redis-master-service.json", - "/Users/gwclark/Vagrant/Centos/chef-repo/cookbooks/apache/attributes/default.rb", - "/Users/gwclark/Vagrant/Centos/chef-repo/cookbooks/apache/templates/default/index.html.erb", - "/Users/gwclark/Vagrant/Centos/chef-repo/cookbooks/apache/templates/default/custom.erb", - "/Users/gwclark/Vagrant/Centos/chef-repo/cookbooks/apache/recipes/default.rb", - "/Users/gwclark/Vagrant/Centos/chef-repo/cookbooks/apache/templates/default/index.html.erb.html", - "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/Default/Default (OSX).sublime-keymap", - "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/Default/Preferences.sublime-settings", - "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/User/Default (OSX).sublime-keymap", - "/Users/gwclark/Git/swagger/swagger-ui/README.md", - "/Users/gwclark/Downloads/jquery-ui-1.10.4/jquery-1.10.2.js", - "/Users/gwclark/Downloads/jQuery-Plugin-For-Drag-Drop-Multi-Select-List-Box-fieldChooser/README.md", - "/Users/gwclark/Downloads/jQuery-Plugin-For-Drag-Drop-Multi-Select-List-Box-fieldChooser/fieldChooser.jquery.json", - "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_perforce_2000/IMS/working/RDG/Processing/Dependency_Map/Drawing#1.pde", - "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_perforce_2000/IMS/working/RDG/Processing/Dependency_Map/App_Server#1.pde", - "/Users/gwclark/Git/hydra/nodejs_server/app1.bom", - "/Users/gwclark/Git/hydra/nodejs_server/ihub_database.service", - "/Users/gwclark/Git/hydra/nodejs_server/app1.service", - "/Users/gwclark/Git/hydra/nodejs_server/service1.service", - "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_perforce_2000/Quickbooks/Main/Source/QB/Platform/Online/Chaos/_qbchaos#1.def", - "/Users/gwclark/Git/cto/common-library/pom.xml", - "/private/tmp/gwclark(p4v)/hlhdsmscap01.corp.intuit.net_scm.payments.intuit.com_1666/ihub/dev/ihub-dev1/puppet/overlays/web/workers.properties#9.erb", - "/Users/gwclark/Downloads/Magic.tcx", - "/Users/gwclark/Magic.xml", - "/Users/gwclark/Felton Loop.tcx", - "/Users/gwclark/Magic.tcx", - "/Users/gwclark/Downloads/examples/examples/01.01.loancalc.html", - "/Users/gwclark/foo.html", - "/Users/gwclark/Downloads/whp-tomcat-7/conf/catalina.properties", - "/app/jenkins3/workspace/transaction-trunk-qa/pom.xml", - "/app/jenkins3/workspace/transaction-trunk-qa/pom", - "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_perforce_2000/IMS/transaction/build/src/main/script/setConfigRepoDirs#1.groovy", - "/Volumes/GARMIN/Garmin/Settings/Settings.fit", - "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_scm.payments.intuit.com_1666/ihub/dev/ihub-dev1/puppet/overlays/app_tomcat2/mytomcat#1.env", - "/private/tmp/gwclark(p4v)/MTVL11b15f099.local_scm.payments.intuit.com_1666/ihub/dev/ihub-dev1/puppet/overlays/app_cdm/cdmUsers#2.properties", - "/Users/gwclark/Perforce/PSD/DevOps/Monitoring/Jamoon/SNMP-Connector/v3-multiple-users.py", - "/Users/gwclark/bin/MuleStudio/plugins/org.mule.tooling.ui.contribution.esper_1.0.0.201307051648/plugin.xml", - "/Users/gwclark/jamoon/pom.xml", - "/Users/gwclark/Git/facs/message.txt", - "/Users/gwclark/Vagrant/Centos/Vagrantfile", - "/Users/gwclark/.ssh/id_rsa.pub", - "/Users/gwclark/maven_setup/shit.py", - "/Users/gwclark/maven_setup/sbd-buildserver-settings.xml", - "/Volumes/gwclark/Library/Scripts/foo", - "/Users/gwclark/Git/salt/salt/loader.py", - "/Users/gwclark/centra.wsdl", - "/Users/gwclark/savon.rb", - "/Users/gwclark/Documents/seekat.process", - "/Users/gwclark/Library/Caches/TemporaryItems/Outlook Temp/props.conf", - "/Users/gwclark/Library/Application Support/Sublime Text 2/Packages/User/Plain text.sublime-settings", - "/Users/gwclark/Vagrant/Centos/ihubtojson.py", - "/Users/gwclark/Vagrant/Centos/samplewonewlines.txt", - "/Users/gwclark/Vagrant/Centos/shit", - "/Users/gwclark/Vagrant/Centos/samplealljson.txt", - "/Users/gwclark/Vagrant/Centos/fuckk", - "/Users/gwclark/Vagrant/Centos/fuck.json", - "/Users/gwclark/Vagrant/Centos/fuck", - "/Users/gwclark/Vagrant/Centos/sample.mmcdmServiceRequestAuditJSON.log.json", - "/Users/gwclark/Vagrant/Centos/sample.mmcdmServiceRequestAuditJSON.log.txt", - "/Users/gwclark/Vagrant/Centos/samplewoxml.txt", - "/Users/gwclark/Desktop/Pupp-Chef Notes", - "/Users/gwclark/.m2/settings.xml", - "/Users/gwclark/Git/yum-test/pom.xml", - "/Users/gwclark/.m2/local-settings.xml", - "/Users/gwclark/.m2/psd-dev-settings.xml", - "/Users/gwclark/Vagrant/Centos/salt/minion", - "/Users/gwclark/buildrouter/orchestration_router.py", - "/Library/Python/2.7/site-packages/salt/minion.py", - "/Users/gwclark/buildrouter/job_event.py", - "/Users/gwclark/buildrouter/push_deployment_job.py", - "/Users/gwclark/buildrouter/jenkins_job.py", - "/Users/gwclark/buildrouter/new_node_runner.py", - "/Users/gwclark/buildrouter/run_job.py", - "/Users/gwclark/buildrouter/jenkinsrunner.py", - "/Users/gwclark/buildrouter/job.json", - "/Users/gwclark/Downloads/pyke-1.1.1/examples/knapsack/test.py", - "/Users/gwclark/buildrouter/push_job.py", - "/Users/gwclark/buildrouter/buildrouter.py", - "/Users/gwclark/buildrouter/jenkinsrunner..py", - "/Users/gwclark/buildrouter/client.py", - "/Users/gwclark/server.py", - "/Users/gwclark/client.py", - "/Users/gwclark/foo", - "/Users/gwclark/Git/salt-cloud/saltcloud/clouds/ipc.py", - "/Users/gwclark/Git/salt-cloud/saltcloud/clouds/ec2.py", - "/Users/gwclark/Git/salt-cloud/requirements.txt", - "/Users/gwclark/Git/salt-cloud/setup.py" - ], - "find": - { - "height": 35.0 - }, - "find_in_files": - { - "height": 0.0, - "where_history": - [ - "" - ] - }, - "find_state": - { - "case_sensitive": true, - "find_history": - [ - "ConfigurationError", - "have any configuration", - "have any configuratin", - "doesn't have any", - "isinsta", - "cy_names", - "Container", - "stream", - "log", - "get_dependency_name", - "from_ps", - "one-off", - "one_off", - "get_contain", - ":\n", - "_get_volue", - "get_volumes_from", - "print", - "print_func", - "print", - "print_func", - "_get_net", - "print_func", - "get_services/", - "print", - "get_services", - "get_services/", - "get_services", - "NAME", - "is_valid", - "get_container_name", - "sys", - "get_container_name", - "get_volumes_from", - "_get_volumes_from", - "get_net", - "container:", - "container", - "get_contain", - "get_volume", - "_get_vol", - "Client", - "mock_clie", - "spec=Client", - "links", - "get_volu", - "_get_volu", - "create_ser", - "create_ser//", - "create_ser/", - "create_ser", - "del", - "to_self", - "one-off", - "to_self", - "/", - "to_self", - "kill", - "_get_ne", - "scale", - "start_or_create_con", - "start_or_create_cond", - "_get_l", - "Creating", - "containers", - "get_containers", - "_get_containers", - "_get_net", - "_get_link", - "project =", - "project", - "if client", - "client =", - "NoSuch", - "inject_dep", - "print", - "NoSuch", - "No suc", - "net =", - "get_depe", - "net = ", - "linked", - "False", - "False///////", - "False//////", - "False/////", - "False////", - "False///", - "False//", - "False/", - "False", - "get_volumes_", - "_get_volumes_from", - "start_links", - "has a depende", - "volumes_from", - "Unsupport", - "dependencies", - "visit", - "link", - "get_dependency_names", - "get_depen", - "get_dependency_name", - "_inject", - "/include_dep", - "get_depen", - "link", - "links", - "links//", - "links/", - "links", - "_get_volumes", - "link", - "link_name", - "get_linked", - "link_to", - "links", - "recreate_container", - "volumes", - "volumes_from", - "Unsupported config", - "link", - "link/", - "link", - "get_linked_names", - "link_name", - "line_name", - "link_to_self" - ], - "highlight": true, - "in_selection": false, - "preserve_case": false, - "regex": false, - "replace_history": - [ - "dependant", - "dependency", - "dependencies", - "dependency_name", - "get_dependency", - "dep_to", - "dependencies", - "get_dependency_names", - "dependency_name", - "dep_to_self", - "dependencies" - ], - "reverse": false, - "show_context": true, - "use_buffer2": true, - "whole_word": false, - "wrap": true - }, - "groups": - [ - { - "sheets": - [ - ] - }, - { - "sheets": - [ - ] - } - ], - "incremental_find": - { - "height": 34.0 - }, - "input": - { - "height": 0.0 - }, - "layout": - { - "cells": - [ - [ - 0, - 0, - 1, - 1 - ], - [ - 1, - 0, - 2, - 1 - ] - ], - "cols": - [ - 0.0, - 0.957747186495, - 1.0 - ], - "rows": - [ - 0.0, - 1.0 - ] - }, - "menu_visible": true, - "replace": - { - "height": 64.0 - }, - "save_all_on_build": true, - "select_file": - { - "height": 0.0, - "selected_items": - [ - [ - "", - "/Users/gwclark/Git/salt-cloud/saltcloud/clouds/ipc.py" - ], - [ - "list", - "/Users/gwclark/Git/salt-cloud/saltcloud/clouds/ipc.py" - ] - ], - "width": 0.0 - }, - "select_project": - { - "height": 0.0, - "selected_items": - [ - ], - "width": 0.0 - }, - "show_minimap": true, - "show_open_files": true, - "show_tabs": true, - "side_bar_visible": true, - "side_bar_width": 194.0, - "status_bar_visible": true -} diff --git a/fig/__init__.py b/fig/__init__.py index 8f3b0c94e26..a7b29e0c909 100644 --- a/fig/__init__.py +++ b/fig/__init__.py @@ -1,4 +1,4 @@ from __future__ import unicode_literals from .service import Service # noqa:flake8 -__version__ = '1.0.1-wdeps' +__version__ = '1.0.1' diff --git a/fig/project.py b/fig/project.py index ad39e152c0e..241f8c35f56 100644 --- a/fig/project.py +++ b/fig/project.py @@ -184,16 +184,11 @@ def get_net(self, service_dict): net_name = get_service_name_from_net(service_dict.get('net')) if net_name: - net = [] try: - service = self.get_service(net_name) - net.append(service) - del service_dict['net'] - return net + net = self.get_service(net_name) except NoSuchService: try: - container = Container.from_id(self.client, net_name) - net.append(container) + net = Container.from_id(self.client, net_name) except APIError: raise ConfigurationError('Serivce "%s" is trying to use the network of "%s", which is not the name of a service or container.' % (service_dict['name'], net_name)) else: @@ -256,10 +251,11 @@ def containers(self, service_names=None, stopped=False, one_off=False): if service.has_container(container, one_off=one_off)] def _inject_deps(self, acc, service): - dep_names = service.get_linked_names() + \ - service.get_volumes_from_names() + \ - service.get_net_names() + \ - service.get_depends_on_names() + net_name = service.get_net_name() + dep_names = (service.get_linked_names() + + service.get_volumes_from_names() + + ([net_name] if net_name else []) + + service.get_depends_on_names()) if len(dep_names) > 0: dep_services = self.get_services( diff --git a/fig/service.py b/fig/service.py index 67b514579cb..4208548e83c 100644 --- a/fig/service.py +++ b/fig/service.py @@ -73,7 +73,7 @@ def __init__(self, name, client=None, project='default', links=None, volumes_fro self.links = links or [] self.volumes_from = volumes_from or [] self.depends_on = depends_on or [] - self.net = net or '' + self.net = net or None self.options = options def containers(self, stopped=False, one_off=False): @@ -293,11 +293,11 @@ def get_volumes_from_names(self): def get_depends_on_names(self): return [s.name for s in self.depends_on] - def get_net_names(self): - if isinstance(self.net, list): - return [s.name for s in self.net if isinstance(s, Service)] + def get_net_name(self): + if isinstance(self.net, Service): + return self.net.name else: - return [] + return def _next_container_name(self, all_containers, one_off=False): bits = [self.project, self.name] @@ -328,7 +328,10 @@ def _get_volumes_from(self, intermediate_container=None): for volume_source in self.volumes_from: if isinstance(volume_source, Service): containers = volume_source.containers(stopped=True) - volumes_from.extend(map(attrgetter('id'), containers)) + if not containers: + volumes_from.append(volume_source.create_container().id) + else: + volumes_from.extend(map(attrgetter('id'), containers)) elif isinstance(volume_source, Container): volumes_from.append(volume_source.id) @@ -339,17 +342,18 @@ def _get_volumes_from(self, intermediate_container=None): return volumes_from def _get_net(self): - net = 'bridge' - if isinstance(self.net, list): - net_source = self.net[0] - if isinstance(net_source, Service): - if len(net_source.containers()) > 0: - container = net_source.containers()[0] - net = 'container:' + container.id - else: - net = 'bridge' - elif isinstance(net_source, Container): - net = 'container:' + net_source.id + if not self.net: + return "bridge" + + if isinstance(self.net, Service): + containers = self.net.containers() + if len(containers) > 0: + net = 'container:' + containers[0].id + else: + log.warning("Warning: Service %s is trying to use reuse the network stack of another service that is not running." % (self.net.name)) + net = None + elif isinstance(self.net, Container): + net = 'container:' + self.net.id else: net = self.net diff --git a/tests/unit/service_test.py b/tests/unit/service_test.py index ab136d41a44..a59018091b8 100644 --- a/tests/unit/service_test.py +++ b/tests/unit/service_test.py @@ -82,6 +82,18 @@ def test_get_volumes_from_service_container_exists(self): self.assertEqual(service._get_volumes_from(), container_ids) + def test_get_volumes_from_service_no_container(self): + container_id = 'abababab' + from_service = mock.create_autospec(Service) + from_service.containers.return_value = [] + from_service.create_container.return_value = mock.Mock( + id=container_id, + spec=Container) + service = Service('test', volumes_from=[from_service]) + + self.assertEqual(service._get_volumes_from(), [container_id]) + from_service.create_container.assert_called_once_with() + def test_split_port_with_host_ip(self): internal_port, external_port = split_port("127.0.0.1:1000:2000") self.assertEqual(internal_port, "2000") From 26ace153247c8e387d8cbf52f828be3c34f6be3a Mon Sep 17 00:00:00 2001 From: Gil Clark Date: Tue, 3 Mar 2015 12:46:19 -0800 Subject: [PATCH 3/3] Removed depends_on. Signed-off-by: Gil Clark --- compose/project.py | 21 ++------------ compose/service.py | 6 +--- tests/integration/project_test.py | 47 ++++++++++++++++++++++++------- tests/unit/sort_service_test.py | 7 +---- 4 files changed, 42 insertions(+), 39 deletions(-) diff --git a/compose/project.py b/compose/project.py index 8c31dc8c0cb..794ef2b6556 100644 --- a/compose/project.py +++ b/compose/project.py @@ -36,7 +36,6 @@ def get_service_dependents(service_dict, services): service for service in services if (name in get_service_names(service.get('links', [])) or name in service.get('volumes_from', []) or - name in service.get('depends_on', []) or name == get_service_name_from_net(service.get('net'))) ] @@ -81,10 +80,9 @@ def from_dicts(cls, name, service_dicts, client): links = project.get_links(service_dict) volumes_from = project.get_volumes_from(service_dict) net = project.get_net(service_dict) - depends_on = project.get_depends_on(service_dict) - project.services.append(Service(client=client, project=name, links=links, net=net, volumes_from=volumes_from, - depends_on=depends_on, **service_dict)) + project.services.append(Service(client=client, project=name, links=links, net=net, + volumes_from=volumes_from, **service_dict)) return project @classmethod @@ -169,18 +167,6 @@ def get_volumes_from(self, service_dict): del service_dict['volumes_from'] return volumes_from - def get_depends_on(self, service_dict): - depends_on = [] - if 'depends_on' in service_dict: - for dep_name in service_dict.get('depends_on', []): - try: - service = self.get_service(dep_name) - depends_on.append(service) - except NoSuchService: - raise ConfigurationError('Service "%s" depends on "%s", which is not the name of a service.' % (service_dict['name'], dep_name)) - del service_dict['depends_on'] - return depends_on - def get_net(self, service_dict): if 'net' in service_dict: net_name = get_service_name_from_net(service_dict.get('net')) @@ -268,8 +254,7 @@ 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 []) + - service.get_depends_on_names()) + ([net_name] if net_name else [])) if len(dep_names) > 0: dep_services = self.get_services( diff --git a/compose/service.py b/compose/service.py index 61bcf08bd81..377198cf486 100644 --- a/compose/service.py +++ b/compose/service.py @@ -88,7 +88,7 @@ class ConfigError(ValueError): class Service(object): - def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, depends_on=None, net=None, **options): + def __init__(self, name, client=None, project='default', links=None, external_links=None, volumes_from=None, net=None, **options): if not re.match('^%s+$' % VALID_NAME_CHARS, name): raise ConfigError('Invalid service name "%s" - only %s are allowed' % (name, VALID_NAME_CHARS)) if not re.match('^%s+$' % VALID_NAME_CHARS, project): @@ -116,7 +116,6 @@ def __init__(self, name, client=None, project='default', links=None, external_li self.links = links or [] self.external_links = external_links or [] self.volumes_from = volumes_from or [] - self.depends_on = depends_on or [] self.net = net or None self.options = options @@ -368,9 +367,6 @@ def get_linked_names(self): def get_volumes_from_names(self): return [s.name for s in self.volumes_from if isinstance(s, Service)] - def get_depends_on_names(self): - return [s.name for s in self.depends_on] - def get_net_name(self): if isinstance(self.net, Service): return self.net.name diff --git a/tests/integration/project_test.py b/tests/integration/project_test.py index fb7c4d00f20..17b54daeeaf 100644 --- a/tests/integration/project_test.py +++ b/tests/integration/project_test.py @@ -277,7 +277,7 @@ def test_project_up_starts_depends(self): 'image': 'busybox:latest', 'command': ["/bin/sleep", "300"], 'net': 'container:net', - 'depends_on': ['app'] + 'links': ['app'] }, }, client=self.client, @@ -296,19 +296,46 @@ def test_project_up_starts_depends(self): project.remove_stopped() def test_project_up_with_no_deps(self): - console = self.create_service('console') - db = self.create_service('db', volumes=['/var/db']) - web = self.create_service('web', links=[(db, 'db')]) - - project = Project('composetest', [web, db, console], self.client) + project = Project.from_config( + name='composetest', + config={ + 'console': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + }, + 'net' : { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"] + }, + 'vol': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'volumes': ["/tmp"] + }, + 'app': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'net': 'container:net' + }, + 'web': { + 'image': 'busybox:latest', + 'command': ["/bin/sleep", "300"], + 'net': 'container:net', + 'links': ['app'], + 'volumes_from': ['vol'] + }, + }, + client=self.client, + ) project.start() self.assertEqual(len(project.containers()), 0) project.up(['web'], start_deps=False) - self.assertEqual(len(project.containers()), 1) - self.assertEqual(len(web.containers()), 1) - self.assertEqual(len(db.containers()), 0) - self.assertEqual(len(console.containers()), 0) + self.assertEqual(len(project.containers(stopped=True)), 2) + self.assertEqual(len(project.get_service('web').containers()), 1) + self.assertEqual(len(project.get_service('vol').containers(stopped=True)), 1) + self.assertEqual(len(project.get_service('net').containers()), 0) + self.assertEqual(len(project.get_service('console').containers()), 0) project.kill() project.remove_stopped() diff --git a/tests/unit/sort_service_test.py b/tests/unit/sort_service_test.py index dfc2b665cc4..f42a947484a 100644 --- a/tests/unit/sort_service_test.py +++ b/tests/unit/sort_service_test.py @@ -130,10 +130,6 @@ def test_sort_service_dicts_6(self): def test_sort_service_dicts_7(self): services = [ - { - 'depends_on': ['four'], - 'name': 'five' - }, { 'net': 'container:three', 'name': 'four' @@ -152,12 +148,11 @@ def test_sort_service_dicts_7(self): ] sorted_services = sort_service_dicts(services) - self.assertEqual(len(sorted_services), 5) + self.assertEqual(len(sorted_services), 4) self.assertEqual(sorted_services[0]['name'], 'one') self.assertEqual(sorted_services[1]['name'], 'two') self.assertEqual(sorted_services[2]['name'], 'three') self.assertEqual(sorted_services[3]['name'], 'four') - self.assertEqual(sorted_services[4]['name'], 'five') def test_sort_service_dicts_circular_imports(self): services = [