diff --git a/fig/project.py b/fig/project.py index 569df38d580..063fde1ddbe 100644 --- a/fig/project.py +++ b/fig/project.py @@ -27,7 +27,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', []))] + dependents = [m for m in services if (n['name'] in get_service_names(m.get('links', []))) or (n['name'] in m.get('volumes_from', [])) or (n['name'] == m.get('inherits', ''))] for m in dependents: visit(m) temporary_marked.remove(n['name']) @@ -58,8 +58,9 @@ 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) + parent = project.get_parent(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, volumes_from=volumes_from, parent=parent, **service_dict)) return project @classmethod @@ -113,6 +114,17 @@ def get_services(self, service_names=None, include_links=False): [uniques.append(s) for s in services if s not in uniques] return uniques + def get_parent(self, service_dict): + parent = None + if 'inherits' in service_dict: + parent_name = service_dict.get('inherits', None) + try: + parent = self.get_service(parent_name) + except NoSuchService: + raise ConfigurationError('Service "%s" inherits service "%s" which does not exist.' % (service_dict['name'], parent_name)) + del service_dict['inherits'] + return parent + def get_links(self, service_dict): links = [] if 'links' in service_dict: diff --git a/fig/service.py b/fig/service.py index e6dcf01281b..950f7136921 100644 --- a/fig/service.py +++ b/fig/service.py @@ -14,6 +14,7 @@ log = logging.getLogger(__name__) +DONT_INHERIT = ['ports', 'name', 'command'] DOCKER_CONFIG_KEYS = ['image', 'command', 'hostname', 'domainname', 'user', 'detach', 'stdin_open', 'tty', 'mem_limit', 'ports', 'environment', 'dns', 'volumes', 'entrypoint', 'privileged', 'volumes_from', 'net', 'working_dir'] DOCKER_CONFIG_HINTS = { @@ -50,7 +51,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, parent=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): @@ -74,6 +75,24 @@ def __init__(self, name, client=None, project='default', links=None, volumes_fro self.volumes_from = volumes_from or [] self.options = options + self.inherit(parent) + + def inherit(self, parent): + if parent == None: + return + + self.links.extend(x for x in parent.links if x not in self.links) + self.volumes_from.extend(x for x in parent.volumes_from if x not in self.volumes_from) + + for option in parent.options: + if option not in DONT_INHERIT: + if isinstance(parent.options[option], list): + self.options[option] = self.options.get(option, []) + parent.options[option] + elif isinstance(parent.options[option], dict): + self.options[option] = dict(parent.options[option].items() + self.options.get(option, {}).items()) + elif option not in self.options: + self.options[option] = parent.options[option] + def containers(self, stopped=False, one_off=False): return [Container.from_ps(self.client, container) for container in self.client.containers(all=stopped)