You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@senssoft.apache.org by ar...@apache.org on 2016/11/20 14:16:20 UTC
[44/51] [partial] incubator-senssoft-tap git commit: Initial
containerized docker build for tap+es+distill
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/config/config_schema_v2.0.json
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/config/config_schema_v2.0.json b/env2/lib/python2.7/site-packages/compose/config/config_schema_v2.0.json
new file mode 100644
index 0000000..e84d131
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/config/config_schema_v2.0.json
@@ -0,0 +1,318 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "id": "config_schema_v2.0.json",
+ "type": "object",
+
+ "properties": {
+ "version": {
+ "type": "string"
+ },
+
+ "services": {
+ "id": "#/properties/services",
+ "type": "object",
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "$ref": "#/definitions/service"
+ }
+ },
+ "additionalProperties": false
+ },
+
+ "networks": {
+ "id": "#/properties/networks",
+ "type": "object",
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "$ref": "#/definitions/network"
+ }
+ }
+ },
+
+ "volumes": {
+ "id": "#/properties/volumes",
+ "type": "object",
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "$ref": "#/definitions/volume"
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+
+ "additionalProperties": false,
+
+ "definitions": {
+
+ "service": {
+ "id": "#/definitions/service",
+ "type": "object",
+
+ "properties": {
+ "build": {
+ "oneOf": [
+ {"type": "string"},
+ {
+ "type": "object",
+ "properties": {
+ "context": {"type": "string"},
+ "dockerfile": {"type": "string"},
+ "args": {"$ref": "#/definitions/list_or_dict"}
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
+ "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
+ "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
+ "cgroup_parent": {"type": "string"},
+ "command": {
+ "oneOf": [
+ {"type": "string"},
+ {"type": "array", "items": {"type": "string"}}
+ ]
+ },
+ "container_name": {"type": "string"},
+ "cpu_shares": {"type": ["number", "string"]},
+ "cpu_quota": {"type": ["number", "string"]},
+ "cpuset": {"type": "string"},
+ "depends_on": {"$ref": "#/definitions/list_of_strings"},
+ "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
+ "dns": {"$ref": "#/definitions/string_or_list"},
+ "dns_search": {"$ref": "#/definitions/string_or_list"},
+ "domainname": {"type": "string"},
+ "entrypoint": {
+ "oneOf": [
+ {"type": "string"},
+ {"type": "array", "items": {"type": "string"}}
+ ]
+ },
+ "env_file": {"$ref": "#/definitions/string_or_list"},
+ "environment": {"$ref": "#/definitions/list_or_dict"},
+
+ "expose": {
+ "type": "array",
+ "items": {
+ "type": ["string", "number"],
+ "format": "expose"
+ },
+ "uniqueItems": true
+ },
+
+ "extends": {
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "object",
+
+ "properties": {
+ "service": {"type": "string"},
+ "file": {"type": "string"}
+ },
+ "required": ["service"],
+ "additionalProperties": false
+ }
+ ]
+ },
+
+ "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
+ "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
+ "hostname": {"type": "string"},
+ "image": {"type": "string"},
+ "ipc": {"type": "string"},
+ "labels": {"$ref": "#/definitions/list_or_dict"},
+ "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
+
+ "logging": {
+ "type": "object",
+
+ "properties": {
+ "driver": {"type": "string"},
+ "options": {"type": "object"}
+ },
+ "additionalProperties": false
+ },
+
+ "mac_address": {"type": "string"},
+ "mem_limit": {"type": ["number", "string"]},
+ "memswap_limit": {"type": ["number", "string"]},
+ "network_mode": {"type": "string"},
+
+ "networks": {
+ "oneOf": [
+ {"$ref": "#/definitions/list_of_strings"},
+ {
+ "type": "object",
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "aliases": {"$ref": "#/definitions/list_of_strings"},
+ "ipv4_address": {"type": "string"},
+ "ipv6_address": {"type": "string"}
+ },
+ "additionalProperties": false
+ },
+ {"type": "null"}
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
+ "pid": {"type": ["string", "null"]},
+
+ "ports": {
+ "type": "array",
+ "items": {
+ "type": ["string", "number"],
+ "format": "ports"
+ },
+ "uniqueItems": true
+ },
+
+ "privileged": {"type": "boolean"},
+ "read_only": {"type": "boolean"},
+ "restart": {"type": "string"},
+ "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
+ "shm_size": {"type": ["number", "string"]},
+ "stdin_open": {"type": "boolean"},
+ "stop_signal": {"type": "string"},
+ "tmpfs": {"$ref": "#/definitions/string_or_list"},
+ "tty": {"type": "boolean"},
+ "ulimits": {
+ "type": "object",
+ "patternProperties": {
+ "^[a-z]+$": {
+ "oneOf": [
+ {"type": "integer"},
+ {
+ "type":"object",
+ "properties": {
+ "hard": {"type": "integer"},
+ "soft": {"type": "integer"}
+ },
+ "required": ["soft", "hard"],
+ "additionalProperties": false
+ }
+ ]
+ }
+ }
+ },
+ "user": {"type": "string"},
+ "volumes": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
+ "volume_driver": {"type": "string"},
+ "volumes_from": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
+ "working_dir": {"type": "string"}
+ },
+
+ "dependencies": {
+ "memswap_limit": ["mem_limit"]
+ },
+ "additionalProperties": false
+ },
+
+ "network": {
+ "id": "#/definitions/network",
+ "type": "object",
+ "properties": {
+ "driver": {"type": "string"},
+ "driver_opts": {
+ "type": "object",
+ "patternProperties": {
+ "^.+$": {"type": ["string", "number"]}
+ }
+ },
+ "ipam": {
+ "type": "object",
+ "properties": {
+ "driver": {"type": "string"},
+ "config": {
+ "type": "array"
+ }
+ },
+ "additionalProperties": false
+ },
+ "external": {
+ "type": ["boolean", "object"],
+ "properties": {
+ "name": {"type": "string"}
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false
+ },
+
+ "volume": {
+ "id": "#/definitions/volume",
+ "type": ["object", "null"],
+ "properties": {
+ "driver": {"type": "string"},
+ "driver_opts": {
+ "type": "object",
+ "patternProperties": {
+ "^.+$": {"type": ["string", "number"]}
+ }
+ },
+ "external": {
+ "type": ["boolean", "object"],
+ "properties": {
+ "name": {"type": "string"}
+ }
+ },
+ "additionalProperties": false
+ },
+ "additionalProperties": false
+ },
+
+ "string_or_list": {
+ "oneOf": [
+ {"type": "string"},
+ {"$ref": "#/definitions/list_of_strings"}
+ ]
+ },
+
+ "list_of_strings": {
+ "type": "array",
+ "items": {"type": "string"},
+ "uniqueItems": true
+ },
+
+ "list_or_dict": {
+ "oneOf": [
+ {
+ "type": "object",
+ "patternProperties": {
+ ".+": {
+ "type": ["string", "number", "null"]
+ }
+ },
+ "additionalProperties": false
+ },
+ {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
+ ]
+ },
+
+ "constraints": {
+ "service": {
+ "id": "#/definitions/constraints/service",
+ "anyOf": [
+ {"required": ["build"]},
+ {"required": ["image"]}
+ ],
+ "properties": {
+ "build": {
+ "required": ["context"]
+ }
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/config/environment.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/config/environment.py b/env2/lib/python2.7/site-packages/compose/config/environment.py
new file mode 100644
index 0000000..5d6b5af
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/config/environment.py
@@ -0,0 +1,107 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+import codecs
+import logging
+import os
+
+import six
+
+from ..const import IS_WINDOWS_PLATFORM
+from .errors import ConfigurationError
+
+log = logging.getLogger(__name__)
+
+
+def split_env(env):
+ if isinstance(env, six.binary_type):
+ env = env.decode('utf-8', 'replace')
+ if '=' in env:
+ return env.split('=', 1)
+ else:
+ return env, None
+
+
+def env_vars_from_file(filename):
+ """
+ Read in a line delimited file of environment variables.
+ """
+ if not os.path.exists(filename):
+ raise ConfigurationError("Couldn't find env file: %s" % filename)
+ elif not os.path.isfile(filename):
+ raise ConfigurationError("%s is not a file." % (filename))
+ env = {}
+ for line in codecs.open(filename, 'r', 'utf-8'):
+ line = line.strip()
+ if line and not line.startswith('#'):
+ k, v = split_env(line)
+ env[k] = v
+ return env
+
+
+class Environment(dict):
+ def __init__(self, *args, **kwargs):
+ super(Environment, self).__init__(*args, **kwargs)
+ self.missing_keys = []
+
+ @classmethod
+ def from_env_file(cls, base_dir):
+ def _initialize():
+ result = cls()
+ if base_dir is None:
+ return result
+ env_file_path = os.path.join(base_dir, '.env')
+ try:
+ return cls(env_vars_from_file(env_file_path))
+ except ConfigurationError:
+ pass
+ return result
+ instance = _initialize()
+ instance.update(os.environ)
+ return instance
+
+ @classmethod
+ def from_command_line(cls, parsed_env_opts):
+ result = cls()
+ for k, v in parsed_env_opts.items():
+ # Values from the command line take priority, unless they're unset
+ # in which case they take the value from the system's environment
+ if v is None and k in os.environ:
+ result[k] = os.environ[k]
+ else:
+ result[k] = v
+ return result
+
+ def __getitem__(self, key):
+ try:
+ return super(Environment, self).__getitem__(key)
+ except KeyError:
+ if IS_WINDOWS_PLATFORM:
+ try:
+ return super(Environment, self).__getitem__(key.upper())
+ except KeyError:
+ pass
+ if key not in self.missing_keys:
+ log.warn(
+ "The {} variable is not set. Defaulting to a blank string."
+ .format(key)
+ )
+ self.missing_keys.append(key)
+
+ return ""
+
+ def __contains__(self, key):
+ result = super(Environment, self).__contains__(key)
+ if IS_WINDOWS_PLATFORM:
+ return (
+ result or super(Environment, self).__contains__(key.upper())
+ )
+ return result
+
+ def get(self, key, *args, **kwargs):
+ if IS_WINDOWS_PLATFORM:
+ return super(Environment, self).get(
+ key,
+ super(Environment, self).get(key.upper(), *args, **kwargs)
+ )
+ return super(Environment, self).get(key, *args, **kwargs)
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/config/errors.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/config/errors.py b/env2/lib/python2.7/site-packages/compose/config/errors.py
new file mode 100644
index 0000000..d14cbbd
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/config/errors.py
@@ -0,0 +1,46 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+
+VERSION_EXPLANATION = (
+ 'You might be seeing this error because you\'re using the wrong Compose '
+ 'file version. Either specify a version of "2" (or "2.0") and place your '
+ 'service definitions under the `services` key, or omit the `version` key '
+ 'and place your service definitions at the root of the file to use '
+ 'version 1.\nFor more on the Compose file format versions, see '
+ 'https://docs.docker.com/compose/compose-file/')
+
+
+class ConfigurationError(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return self.msg
+
+
+class DependencyError(ConfigurationError):
+ pass
+
+
+class CircularReference(ConfigurationError):
+ def __init__(self, trail):
+ self.trail = trail
+
+ @property
+ def msg(self):
+ lines = [
+ "{} in {}".format(service_name, filename)
+ for (filename, service_name) in self.trail
+ ]
+ return "Circular reference:\n {}".format("\n extends ".join(lines))
+
+
+class ComposeFileNotFound(ConfigurationError):
+ def __init__(self, supported_filenames):
+ super(ComposeFileNotFound, self).__init__("""
+ Can't find a suitable configuration file in this directory or any
+ parent. Are you in the right directory?
+
+ Supported filenames: %s
+ """ % ", ".join(supported_filenames))
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/config/interpolation.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/config/interpolation.py b/env2/lib/python2.7/site-packages/compose/config/interpolation.py
new file mode 100644
index 0000000..63020d9
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/config/interpolation.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+import logging
+from string import Template
+
+import six
+
+from .errors import ConfigurationError
+log = logging.getLogger(__name__)
+
+
+def interpolate_environment_variables(config, section, environment):
+
+ def process_item(name, config_dict):
+ return dict(
+ (key, interpolate_value(name, key, val, section, environment))
+ for key, val in (config_dict or {}).items()
+ )
+
+ return dict(
+ (name, process_item(name, config_dict or {}))
+ for name, config_dict in config.items()
+ )
+
+
+def interpolate_value(name, config_key, value, section, mapping):
+ try:
+ return recursive_interpolate(value, mapping)
+ except InvalidInterpolation as e:
+ raise ConfigurationError(
+ 'Invalid interpolation format for "{config_key}" option '
+ 'in {section} "{name}": "{string}"'.format(
+ config_key=config_key,
+ name=name,
+ section=section,
+ string=e.string))
+
+
+def recursive_interpolate(obj, mapping):
+ if isinstance(obj, six.string_types):
+ return interpolate(obj, mapping)
+ elif isinstance(obj, dict):
+ return dict(
+ (key, recursive_interpolate(val, mapping))
+ for (key, val) in obj.items()
+ )
+ elif isinstance(obj, list):
+ return [recursive_interpolate(val, mapping) for val in obj]
+ else:
+ return obj
+
+
+def interpolate(string, mapping):
+ try:
+ return Template(string).substitute(mapping)
+ except ValueError:
+ raise InvalidInterpolation(string)
+
+
+class InvalidInterpolation(Exception):
+ def __init__(self, string):
+ self.string = string
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/config/serialize.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/config/serialize.py b/env2/lib/python2.7/site-packages/compose/config/serialize.py
new file mode 100644
index 0000000..b788a55
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/config/serialize.py
@@ -0,0 +1,60 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+import six
+import yaml
+
+from compose.config import types
+from compose.config.config import V1
+from compose.config.config import V2_0
+
+
+def serialize_config_type(dumper, data):
+ representer = dumper.represent_str if six.PY3 else dumper.represent_unicode
+ return representer(data.repr())
+
+
+yaml.SafeDumper.add_representer(types.VolumeFromSpec, serialize_config_type)
+yaml.SafeDumper.add_representer(types.VolumeSpec, serialize_config_type)
+
+
+def denormalize_config(config):
+ denormalized_services = [
+ denormalize_service_dict(service_dict, config.version)
+ for service_dict in config.services
+ ]
+ services = {
+ service_dict.pop('name'): service_dict
+ for service_dict in denormalized_services
+ }
+ networks = config.networks.copy()
+ for net_name, net_conf in networks.items():
+ if 'external_name' in net_conf:
+ del net_conf['external_name']
+
+ return {
+ 'version': V2_0,
+ 'services': services,
+ 'networks': networks,
+ 'volumes': config.volumes,
+ }
+
+
+def serialize_config(config):
+ return yaml.safe_dump(
+ denormalize_config(config),
+ default_flow_style=False,
+ indent=2,
+ width=80)
+
+
+def denormalize_service_dict(service_dict, version):
+ service_dict = service_dict.copy()
+
+ if 'restart' in service_dict:
+ service_dict['restart'] = types.serialize_restart_spec(service_dict['restart'])
+
+ if version == V1 and 'network_mode' not in service_dict:
+ service_dict['network_mode'] = 'bridge'
+
+ return service_dict
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/config/sort_services.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/config/sort_services.py b/env2/lib/python2.7/site-packages/compose/config/sort_services.py
new file mode 100644
index 0000000..20ac446
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/config/sort_services.py
@@ -0,0 +1,72 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+from compose.config.errors import DependencyError
+
+
+def get_service_name_from_network_mode(network_mode):
+ return get_source_name_from_network_mode(network_mode, 'service')
+
+
+def get_container_name_from_network_mode(network_mode):
+ return get_source_name_from_network_mode(network_mode, 'container')
+
+
+def get_source_name_from_network_mode(network_mode, source_type):
+ if not network_mode:
+ return
+
+ if not network_mode.startswith(source_type+':'):
+ return
+
+ _, net_name = network_mode.split(':', 1)
+ return net_name
+
+
+def get_service_names(links):
+ return [link.split(':')[0] for link in links]
+
+
+def get_service_names_from_volumes_from(volumes_from):
+ return [volume_from.source for volume_from in volumes_from]
+
+
+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 get_service_names_from_volumes_from(service.get('volumes_from', [])) or
+ name == get_service_name_from_network_mode(service.get('network_mode')) or
+ name in service.get('depends_on', []))
+ ]
+
+
+def sort_service_dicts(services):
+ # Topological sort (Cormen/Tarjan algorithm).
+ unmarked = services[:]
+ temporary_marked = set()
+ sorted_services = []
+
+ def visit(n):
+ if n['name'] in temporary_marked:
+ if n['name'] in get_service_names(n.get('links', [])):
+ raise DependencyError('A service can not link to itself: %s' % n['name'])
+ if n['name'] in n.get('volumes_from', []):
+ raise DependencyError('A service can not mount itself as volume: %s' % n['name'])
+ if n['name'] in n.get('depends_on', []):
+ raise DependencyError('A service can not depend on itself: %s' % n['name'])
+ raise DependencyError('Circular dependency between %s' % ' and '.join(temporary_marked))
+
+ if n in unmarked:
+ temporary_marked.add(n['name'])
+ for m in get_service_dependents(n, services):
+ visit(m)
+ temporary_marked.remove(n['name'])
+ unmarked.remove(n)
+ sorted_services.insert(0, n)
+
+ while unmarked:
+ visit(unmarked[-1])
+
+ return sorted_services
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/config/types.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/config/types.py b/env2/lib/python2.7/site-packages/compose/config/types.py
new file mode 100644
index 0000000..e6a3dea
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/config/types.py
@@ -0,0 +1,198 @@
+"""
+Types for objects parsed from the configuration.
+"""
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+import os
+from collections import namedtuple
+
+import six
+
+from compose.config.config import V1
+from compose.config.errors import ConfigurationError
+from compose.const import IS_WINDOWS_PLATFORM
+
+
+class VolumeFromSpec(namedtuple('_VolumeFromSpec', 'source mode type')):
+
+ # TODO: drop service_names arg when v1 is removed
+ @classmethod
+ def parse(cls, volume_from_config, service_names, version):
+ func = cls.parse_v1 if version == V1 else cls.parse_v2
+ return func(service_names, volume_from_config)
+
+ @classmethod
+ def parse_v1(cls, service_names, volume_from_config):
+ parts = volume_from_config.split(':')
+ if len(parts) > 2:
+ raise ConfigurationError(
+ "volume_from {} has incorrect format, should be "
+ "service[:mode]".format(volume_from_config))
+
+ if len(parts) == 1:
+ source = parts[0]
+ mode = 'rw'
+ else:
+ source, mode = parts
+
+ type = 'service' if source in service_names else 'container'
+ return cls(source, mode, type)
+
+ @classmethod
+ def parse_v2(cls, service_names, volume_from_config):
+ parts = volume_from_config.split(':')
+ if len(parts) > 3:
+ raise ConfigurationError(
+ "volume_from {} has incorrect format, should be one of "
+ "'<service name>[:<mode>]' or "
+ "'container:<container name>[:<mode>]'".format(volume_from_config))
+
+ if len(parts) == 1:
+ source = parts[0]
+ return cls(source, 'rw', 'service')
+
+ if len(parts) == 2:
+ if parts[0] == 'container':
+ type, source = parts
+ return cls(source, 'rw', type)
+
+ source, mode = parts
+ return cls(source, mode, 'service')
+
+ if len(parts) == 3:
+ type, source, mode = parts
+ if type not in ('service', 'container'):
+ raise ConfigurationError(
+ "Unknown volumes_from type '{}' in '{}'".format(
+ type,
+ volume_from_config))
+
+ return cls(source, mode, type)
+
+ def repr(self):
+ return '{v.type}:{v.source}:{v.mode}'.format(v=self)
+
+
+def parse_restart_spec(restart_config):
+ if not restart_config:
+ return None
+ parts = restart_config.split(':')
+ if len(parts) > 2:
+ raise ConfigurationError(
+ "Restart %s has incorrect format, should be "
+ "mode[:max_retry]" % restart_config)
+ if len(parts) == 2:
+ name, max_retry_count = parts
+ else:
+ name, = parts
+ max_retry_count = 0
+
+ return {'Name': name, 'MaximumRetryCount': int(max_retry_count)}
+
+
+def serialize_restart_spec(restart_spec):
+ parts = [restart_spec['Name']]
+ if restart_spec['MaximumRetryCount']:
+ parts.append(six.text_type(restart_spec['MaximumRetryCount']))
+ return ':'.join(parts)
+
+
+def parse_extra_hosts(extra_hosts_config):
+ if not extra_hosts_config:
+ return {}
+
+ if isinstance(extra_hosts_config, dict):
+ return dict(extra_hosts_config)
+
+ if isinstance(extra_hosts_config, list):
+ extra_hosts_dict = {}
+ for extra_hosts_line in extra_hosts_config:
+ # TODO: validate string contains ':' ?
+ host, ip = extra_hosts_line.split(':', 1)
+ extra_hosts_dict[host.strip()] = ip.strip()
+ return extra_hosts_dict
+
+
+def normalize_paths_for_engine(external_path, internal_path):
+ """Windows paths, c:\my\path\shiny, need to be changed to be compatible with
+ the Engine. Volume paths are expected to be linux style /c/my/path/shiny/
+ """
+ if not IS_WINDOWS_PLATFORM:
+ return external_path, internal_path
+
+ if external_path:
+ drive, tail = os.path.splitdrive(external_path)
+
+ if drive:
+ external_path = '/' + drive.lower().rstrip(':') + tail
+
+ external_path = external_path.replace('\\', '/')
+
+ return external_path, internal_path.replace('\\', '/')
+
+
+class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')):
+
+ @classmethod
+ def parse(cls, volume_config):
+ """Parse a volume_config path and split it into external:internal[:mode]
+ parts to be returned as a valid VolumeSpec.
+ """
+ if IS_WINDOWS_PLATFORM:
+ # relative paths in windows expand to include the drive, eg C:\
+ # so we join the first 2 parts back together to count as one
+ drive, tail = os.path.splitdrive(volume_config)
+ parts = tail.split(":")
+
+ if drive:
+ parts[0] = drive + parts[0]
+ else:
+ parts = volume_config.split(':')
+
+ if len(parts) > 3:
+ raise ConfigurationError(
+ "Volume %s has incorrect format, should be "
+ "external:internal[:mode]" % volume_config)
+
+ if len(parts) == 1:
+ external, internal = normalize_paths_for_engine(
+ None,
+ os.path.normpath(parts[0]))
+ else:
+ external, internal = normalize_paths_for_engine(
+ os.path.normpath(parts[0]),
+ os.path.normpath(parts[1]))
+
+ mode = 'rw'
+ if len(parts) == 3:
+ mode = parts[2]
+
+ return cls(external, internal, mode)
+
+ def repr(self):
+ external = self.external + ':' if self.external else ''
+ return '{ext}{v.internal}:{v.mode}'.format(ext=external, v=self)
+
+ @property
+ def is_named_volume(self):
+ return self.external and not self.external.startswith(('.', '/', '~'))
+
+
+class ServiceLink(namedtuple('_ServiceLink', 'target alias')):
+
+ @classmethod
+ def parse(cls, link_spec):
+ target, _, alias = link_spec.partition(':')
+ if not alias:
+ alias = target
+ return cls(target, alias)
+
+ def repr(self):
+ if self.target == self.alias:
+ return self.target
+ return '{s.target}:{s.alias}'.format(s=self)
+
+ @property
+ def merge_field(self):
+ return self.alias
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/config/validation.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/config/validation.py b/env2/lib/python2.7/site-packages/compose/config/validation.py
new file mode 100644
index 0000000..7452e98
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/config/validation.py
@@ -0,0 +1,421 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+import json
+import logging
+import os
+import re
+import sys
+
+import six
+from docker.utils.ports import split_port
+from jsonschema import Draft4Validator
+from jsonschema import FormatChecker
+from jsonschema import RefResolver
+from jsonschema import ValidationError
+
+from ..const import COMPOSEFILE_V1 as V1
+from .errors import ConfigurationError
+from .errors import VERSION_EXPLANATION
+from .sort_services import get_service_name_from_network_mode
+
+
+log = logging.getLogger(__name__)
+
+
+DOCKER_CONFIG_HINTS = {
+ 'cpu_share': 'cpu_shares',
+ 'add_host': 'extra_hosts',
+ 'hosts': 'extra_hosts',
+ 'extra_host': 'extra_hosts',
+ 'device': 'devices',
+ 'link': 'links',
+ 'memory_swap': 'memswap_limit',
+ 'port': 'ports',
+ 'privilege': 'privileged',
+ 'priviliged': 'privileged',
+ 'privilige': 'privileged',
+ 'volume': 'volumes',
+ 'workdir': 'working_dir',
+}
+
+
+VALID_NAME_CHARS = '[a-zA-Z0-9\._\-]'
+VALID_EXPOSE_FORMAT = r'^\d+(\-\d+)?(\/[a-zA-Z]+)?$'
+
+
+@FormatChecker.cls_checks(format="ports", raises=ValidationError)
+def format_ports(instance):
+ try:
+ split_port(instance)
+ except ValueError as e:
+ raise ValidationError(six.text_type(e))
+ return True
+
+
+@FormatChecker.cls_checks(format="expose", raises=ValidationError)
+def format_expose(instance):
+ if isinstance(instance, six.string_types):
+ if not re.match(VALID_EXPOSE_FORMAT, instance):
+ raise ValidationError(
+ "should be of the format 'PORT[/PROTOCOL]'")
+
+ return True
+
+
+def match_named_volumes(service_dict, project_volumes):
+ service_volumes = service_dict.get('volumes', [])
+ for volume_spec in service_volumes:
+ if volume_spec.is_named_volume and volume_spec.external not in project_volumes:
+ raise ConfigurationError(
+ 'Named volume "{0}" is used in service "{1}" but no'
+ ' declaration was found in the volumes section.'.format(
+ volume_spec.repr(), service_dict.get('name')
+ )
+ )
+
+
+def python_type_to_yaml_type(type_):
+ type_name = type(type_).__name__
+ return {
+ 'dict': 'mapping',
+ 'list': 'array',
+ 'int': 'number',
+ 'float': 'number',
+ 'bool': 'boolean',
+ 'unicode': 'string',
+ 'str': 'string',
+ 'bytes': 'string',
+ }.get(type_name, type_name)
+
+
+def validate_config_section(filename, config, section):
+ """Validate the structure of a configuration section. This must be done
+ before interpolation so it's separate from schema validation.
+ """
+ if not isinstance(config, dict):
+ raise ConfigurationError(
+ "In file '{filename}', {section} must be a mapping, not "
+ "{type}.".format(
+ filename=filename,
+ section=section,
+ type=anglicize_json_type(python_type_to_yaml_type(config))))
+
+ for key, value in config.items():
+ if not isinstance(key, six.string_types):
+ raise ConfigurationError(
+ "In file '{filename}', the {section} name {name} must be a "
+ "quoted string, i.e. '{name}'.".format(
+ filename=filename,
+ section=section,
+ name=key))
+
+ if not isinstance(value, (dict, type(None))):
+ raise ConfigurationError(
+ "In file '{filename}', {section} '{name}' must be a mapping not "
+ "{type}.".format(
+ filename=filename,
+ section=section,
+ name=key,
+ type=anglicize_json_type(python_type_to_yaml_type(value))))
+
+
+def validate_top_level_object(config_file):
+ if not isinstance(config_file.config, dict):
+ raise ConfigurationError(
+ "Top level object in '{}' needs to be an object not '{}'.".format(
+ config_file.filename,
+ type(config_file.config)))
+
+
+def validate_ulimits(service_config):
+ ulimit_config = service_config.config.get('ulimits', {})
+ for limit_name, soft_hard_values in six.iteritems(ulimit_config):
+ if isinstance(soft_hard_values, dict):
+ if not soft_hard_values['soft'] <= soft_hard_values['hard']:
+ raise ConfigurationError(
+ "Service '{s.name}' has invalid ulimit '{ulimit}'. "
+ "'soft' value can not be greater than 'hard' value ".format(
+ s=service_config,
+ ulimit=ulimit_config))
+
+
+def validate_extends_file_path(service_name, extends_options, filename):
+ """
+ The service to be extended must either be defined in the config key 'file',
+ or within 'filename'.
+ """
+ error_prefix = "Invalid 'extends' configuration for %s:" % service_name
+
+ if 'file' not in extends_options and filename is None:
+ raise ConfigurationError(
+ "%s you need to specify a 'file', e.g. 'file: something.yml'" % error_prefix
+ )
+
+
+def validate_network_mode(service_config, service_names):
+ network_mode = service_config.config.get('network_mode')
+ if not network_mode:
+ return
+
+ if 'networks' in service_config.config:
+ raise ConfigurationError("'network_mode' and 'networks' cannot be combined")
+
+ dependency = get_service_name_from_network_mode(network_mode)
+ if not dependency:
+ return
+
+ if dependency not in service_names:
+ raise ConfigurationError(
+ "Service '{s.name}' uses the network stack of service '{dep}' which "
+ "is undefined.".format(s=service_config, dep=dependency))
+
+
+def validate_links(service_config, service_names):
+ for link in service_config.config.get('links', []):
+ if link.split(':')[0] not in service_names:
+ raise ConfigurationError(
+ "Service '{s.name}' has a link to service '{link}' which is "
+ "undefined.".format(s=service_config, link=link))
+
+
+def validate_depends_on(service_config, service_names):
+ for dependency in service_config.config.get('depends_on', []):
+ if dependency not in service_names:
+ raise ConfigurationError(
+ "Service '{s.name}' depends on service '{dep}' which is "
+ "undefined.".format(s=service_config, dep=dependency))
+
+
+def get_unsupported_config_msg(path, error_key):
+ msg = "Unsupported config option for {}: '{}'".format(path_string(path), error_key)
+ if error_key in DOCKER_CONFIG_HINTS:
+ msg += " (did you mean '{}'?)".format(DOCKER_CONFIG_HINTS[error_key])
+ return msg
+
+
+def anglicize_json_type(json_type):
+ if json_type.startswith(('a', 'e', 'i', 'o', 'u')):
+ return 'an ' + json_type
+ return 'a ' + json_type
+
+
+def is_service_dict_schema(schema_id):
+ return schema_id in ('config_schema_v1.json', '#/properties/services')
+
+
+def handle_error_for_schema_with_id(error, path):
+ schema_id = error.schema['id']
+
+ if is_service_dict_schema(schema_id) and error.validator == 'additionalProperties':
+ return "Invalid service name '{}' - only {} characters are allowed".format(
+ # The service_name is the key to the json object
+ list(error.instance)[0],
+ VALID_NAME_CHARS)
+
+ if error.validator == 'additionalProperties':
+ if schema_id == '#/definitions/service':
+ invalid_config_key = parse_key_from_error_msg(error)
+ return get_unsupported_config_msg(path, invalid_config_key)
+
+ if not error.path:
+ return '{}\n\n{}'.format(error.message, VERSION_EXPLANATION)
+
+
+def handle_generic_error(error, path):
+ msg_format = None
+ error_msg = error.message
+
+ if error.validator == 'oneOf':
+ msg_format = "{path} {msg}"
+ config_key, error_msg = _parse_oneof_validator(error)
+ if config_key:
+ path.append(config_key)
+
+ elif error.validator == 'type':
+ msg_format = "{path} contains an invalid type, it should be {msg}"
+ error_msg = _parse_valid_types_from_validator(error.validator_value)
+
+ elif error.validator == 'required':
+ error_msg = ", ".join(error.validator_value)
+ msg_format = "{path} is invalid, {msg} is required."
+
+ elif error.validator == 'dependencies':
+ config_key = list(error.validator_value.keys())[0]
+ required_keys = ",".join(error.validator_value[config_key])
+
+ msg_format = "{path} is invalid: {msg}"
+ path.append(config_key)
+ error_msg = "when defining '{}' you must set '{}' as well".format(
+ config_key,
+ required_keys)
+
+ elif error.cause:
+ error_msg = six.text_type(error.cause)
+ msg_format = "{path} is invalid: {msg}"
+
+ elif error.path:
+ msg_format = "{path} value {msg}"
+
+ if msg_format:
+ return msg_format.format(path=path_string(path), msg=error_msg)
+
+ return error.message
+
+
+def parse_key_from_error_msg(error):
+ return error.message.split("'")[1]
+
+
+def path_string(path):
+ return ".".join(c for c in path if isinstance(c, six.string_types))
+
+
+def _parse_valid_types_from_validator(validator):
+ """A validator value can be either an array of valid types or a string of
+ a valid type. Parse the valid types and prefix with the correct article.
+ """
+ if not isinstance(validator, list):
+ return anglicize_json_type(validator)
+
+ if len(validator) == 1:
+ return anglicize_json_type(validator[0])
+
+ return "{}, or {}".format(
+ ", ".join([anglicize_json_type(validator[0])] + validator[1:-1]),
+ anglicize_json_type(validator[-1]))
+
+
+def _parse_oneof_validator(error):
+ """oneOf has multiple schemas, so we need to reason about which schema, sub
+ schema or constraint the validation is failing on.
+ Inspecting the context value of a ValidationError gives us information about
+ which sub schema failed and which kind of error it is.
+ """
+ types = []
+ for context in error.context:
+
+ if context.validator == 'oneOf':
+ _, error_msg = _parse_oneof_validator(context)
+ return path_string(context.path), error_msg
+
+ if context.validator == 'required':
+ return (None, context.message)
+
+ if context.validator == 'additionalProperties':
+ invalid_config_key = parse_key_from_error_msg(context)
+ return (None, "contains unsupported option: '{}'".format(invalid_config_key))
+
+ if context.path:
+ return (
+ path_string(context.path),
+ "contains {}, which is an invalid type, it should be {}".format(
+ json.dumps(context.instance),
+ _parse_valid_types_from_validator(context.validator_value)),
+ )
+
+ if context.validator == 'uniqueItems':
+ return (
+ None,
+ "contains non unique items, please remove duplicates from {}".format(
+ context.instance),
+ )
+
+ if context.validator == 'type':
+ types.append(context.validator_value)
+
+ valid_types = _parse_valid_types_from_validator(types)
+ return (None, "contains an invalid type, it should be {}".format(valid_types))
+
+
+def process_service_constraint_errors(error, service_name, version):
+ if version == V1:
+ if 'image' in error.instance and 'build' in error.instance:
+ return (
+ "Service {} has both an image and build path specified. "
+ "A service can either be built to image or use an existing "
+ "image, not both.".format(service_name))
+
+ if 'image' in error.instance and 'dockerfile' in error.instance:
+ return (
+ "Service {} has both an image and alternate Dockerfile. "
+ "A service can either be built to image or use an existing "
+ "image, not both.".format(service_name))
+
+ if 'image' not in error.instance and 'build' not in error.instance:
+ return (
+ "Service {} has neither an image nor a build context specified. "
+ "At least one must be provided.".format(service_name))
+
+
+def process_config_schema_errors(error):
+ path = list(error.path)
+
+ if 'id' in error.schema:
+ error_msg = handle_error_for_schema_with_id(error, path)
+ if error_msg:
+ return error_msg
+
+ return handle_generic_error(error, path)
+
+
+def validate_against_config_schema(config_file):
+ schema = load_jsonschema(config_file.version)
+ format_checker = FormatChecker(["ports", "expose"])
+ validator = Draft4Validator(
+ schema,
+ resolver=RefResolver(get_resolver_path(), schema),
+ format_checker=format_checker)
+ handle_errors(
+ validator.iter_errors(config_file.config),
+ process_config_schema_errors,
+ config_file.filename)
+
+
+def validate_service_constraints(config, service_name, version):
+ def handler(errors):
+ return process_service_constraint_errors(errors, service_name, version)
+
+ schema = load_jsonschema(version)
+ validator = Draft4Validator(schema['definitions']['constraints']['service'])
+ handle_errors(validator.iter_errors(config), handler, None)
+
+
+def get_schema_path():
+ return os.path.dirname(os.path.abspath(__file__))
+
+
+def load_jsonschema(version):
+ filename = os.path.join(
+ get_schema_path(),
+ "config_schema_v{0}.json".format(version))
+
+ with open(filename, "r") as fh:
+ return json.load(fh)
+
+
+def get_resolver_path():
+ schema_path = get_schema_path()
+ if sys.platform == "win32":
+ scheme = "///"
+ # TODO: why is this necessary?
+ schema_path = schema_path.replace('\\', '/')
+ else:
+ scheme = "//"
+ return "file:{}{}/".format(scheme, schema_path)
+
+
+def handle_errors(errors, format_error_func, filename):
+ """jsonschema returns an error tree full of information to explain what has
+ gone wrong. Process each error and pull out relevant information and re-write
+ helpful error messages that are relevant.
+ """
+ errors = list(sorted(errors, key=str))
+ if not errors:
+ return
+
+ error_msg = '\n'.join(format_error_func(error) for error in errors)
+ raise ConfigurationError(
+ "The Compose file{file_msg} is invalid because:\n{error_msg}".format(
+ file_msg=" '{}'".format(filename) if filename else "",
+ error_msg=error_msg))
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/const.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/const.py b/env2/lib/python2.7/site-packages/compose/const.py
new file mode 100644
index 0000000..b930e0b
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/const.py
@@ -0,0 +1,28 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+import sys
+
+DEFAULT_TIMEOUT = 10
+HTTP_TIMEOUT = 60
+IMAGE_EVENTS = ['delete', 'import', 'pull', 'push', 'tag', 'untag']
+IS_WINDOWS_PLATFORM = (sys.platform == "win32")
+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'
+LABEL_CONFIG_HASH = 'com.docker.compose.config-hash'
+
+COMPOSEFILE_V1 = '1'
+COMPOSEFILE_V2_0 = '2.0'
+
+API_VERSIONS = {
+ COMPOSEFILE_V1: '1.21',
+ COMPOSEFILE_V2_0: '1.22',
+}
+
+API_VERSION_TO_ENGINE_VERSION = {
+ API_VERSIONS[COMPOSEFILE_V1]: '1.9.0',
+ API_VERSIONS[COMPOSEFILE_V2_0]: '1.10.0'
+}
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/container.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/container.py b/env2/lib/python2.7/site-packages/compose/container.py
new file mode 100644
index 0000000..2c16863
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/container.py
@@ -0,0 +1,272 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+from functools import reduce
+
+import six
+
+from .const import LABEL_CONTAINER_NUMBER
+from .const import LABEL_PROJECT
+from .const import LABEL_SERVICE
+
+
+class Container(object):
+ """
+ Represents a Docker container, constructed from the output of
+ GET /containers/:id:/json.
+ """
+ def __init__(self, client, dictionary, has_been_inspected=False):
+ self.client = client
+ self.dictionary = dictionary
+ self.has_been_inspected = has_been_inspected
+ self.log_stream = None
+
+ @classmethod
+ def from_ps(cls, client, dictionary, **kwargs):
+ """
+ Construct a container object from the output of GET /containers/json.
+ """
+ name = get_container_name(dictionary)
+ if name is None:
+ return None
+
+ new_dictionary = {
+ 'Id': dictionary['Id'],
+ 'Image': dictionary['Image'],
+ 'Name': '/' + name,
+ }
+ return cls(client, new_dictionary, **kwargs)
+
+ @classmethod
+ def from_id(cls, client, id):
+ return cls(client, client.inspect_container(id), has_been_inspected=True)
+
+ @classmethod
+ def create(cls, client, **options):
+ response = client.create_container(**options)
+ return cls.from_id(client, response['Id'])
+
+ @property
+ def id(self):
+ return self.dictionary['Id']
+
+ @property
+ def image(self):
+ return self.dictionary['Image']
+
+ @property
+ def image_config(self):
+ return self.client.inspect_image(self.image)
+
+ @property
+ def short_id(self):
+ return self.id[:12]
+
+ @property
+ def name(self):
+ return self.dictionary['Name'][1:]
+
+ @property
+ def service(self):
+ return self.labels.get(LABEL_SERVICE)
+
+ @property
+ def name_without_project(self):
+ project = self.labels.get(LABEL_PROJECT)
+
+ if self.name.startswith('{0}_{1}'.format(project, self.service)):
+ return '{0}_{1}'.format(self.service, self.number)
+ else:
+ return self.name
+
+ @property
+ def number(self):
+ 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):
+ self.inspect_if_not_inspected()
+ return self.get('NetworkSettings.Ports') or {}
+
+ @property
+ def human_readable_ports(self):
+ def format_port(private, public):
+ if not public:
+ return private
+ return '{HostIp}:{HostPort}->{private}'.format(
+ private=private, **public[0])
+
+ return ', '.join(format_port(*item)
+ for item in sorted(six.iteritems(self.ports)))
+
+ @property
+ def labels(self):
+ return self.get('Config.Labels') or {}
+
+ @property
+ def stop_signal(self):
+ return self.get('Config.StopSignal')
+
+ @property
+ def log_config(self):
+ return self.get('HostConfig.LogConfig') or None
+
+ @property
+ def human_readable_state(self):
+ if self.is_paused:
+ return 'Paused'
+ if self.is_restarting:
+ return 'Restarting'
+ if self.is_running:
+ return 'Ghost' if self.get('State.Ghost') else 'Up'
+ else:
+ return 'Exit %s' % self.get('State.ExitCode')
+
+ @property
+ def human_readable_command(self):
+ entrypoint = self.get('Config.Entrypoint') or []
+ cmd = self.get('Config.Cmd') or []
+ return ' '.join(entrypoint + cmd)
+
+ @property
+ def environment(self):
+ def parse_env(var):
+ if '=' in var:
+ return var.split("=", 1)
+ return var, None
+ return dict(parse_env(var) for var in self.get('Config.Env') or [])
+
+ @property
+ def exit_code(self):
+ return self.get('State.ExitCode')
+
+ @property
+ def is_running(self):
+ return self.get('State.Running')
+
+ @property
+ def is_restarting(self):
+ return self.get('State.Restarting')
+
+ @property
+ def is_paused(self):
+ return self.get('State.Paused')
+
+ @property
+ def log_driver(self):
+ return self.get('HostConfig.LogConfig.Type')
+
+ @property
+ def has_api_logs(self):
+ log_type = self.log_driver
+ return not log_type or log_type != 'none'
+
+ def attach_log_stream(self):
+ """A log stream can only be attached if the container uses a json-file
+ log driver.
+ """
+ if self.has_api_logs:
+ self.log_stream = self.attach(stdout=True, stderr=True, stream=True)
+
+ def get(self, key):
+ """Return a value from the container or None if the value is not set.
+
+ :param key: a string using dotted notation for nested dictionary
+ lookups
+ """
+ self.inspect_if_not_inspected()
+
+ def get_value(dictionary, key):
+ return (dictionary or {}).get(key)
+
+ return reduce(get_value, key.split('.'), self.dictionary)
+
+ def get_local_port(self, port, protocol='tcp'):
+ port = self.ports.get("%s/%s" % (port, protocol))
+ return "{HostIp}:{HostPort}".format(**port[0]) if port else None
+
+ def get_mount(self, mount_dest):
+ for mount in self.get('Mounts'):
+ if mount['Destination'] == mount_dest:
+ return mount
+ return None
+
+ def start(self, **options):
+ return self.client.start(self.id, **options)
+
+ def stop(self, **options):
+ return self.client.stop(self.id, **options)
+
+ def pause(self, **options):
+ return self.client.pause(self.id, **options)
+
+ def unpause(self, **options):
+ return self.client.unpause(self.id, **options)
+
+ def kill(self, **options):
+ return self.client.kill(self.id, **options)
+
+ def restart(self, **options):
+ return self.client.restart(self.id, **options)
+
+ def remove(self, **options):
+ return self.client.remove_container(self.id, **options)
+
+ def create_exec(self, command, **options):
+ return self.client.exec_create(self.id, command, **options)
+
+ def start_exec(self, exec_id, **options):
+ return self.client.exec_start(exec_id, **options)
+
+ def rename_to_tmp_name(self):
+ """Rename the container to a hopefully unique temporary container name
+ by prepending the short id.
+ """
+ self.client.rename(
+ self.id,
+ '%s_%s' % (self.short_id, self.name)
+ )
+
+ def inspect_if_not_inspected(self):
+ if not self.has_been_inspected:
+ self.inspect()
+
+ def wait(self):
+ return self.client.wait(self.id)
+
+ def logs(self, *args, **kwargs):
+ return self.client.logs(self.id, *args, **kwargs)
+
+ def inspect(self):
+ self.dictionary = self.client.inspect_container(self.id)
+ self.has_been_inspected = True
+ return self.dictionary
+
+ def attach(self, *args, **kwargs):
+ return self.client.attach(self.id, *args, **kwargs)
+
+ def __repr__(self):
+ 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'):
+ return None
+ # inspect
+ if 'Name' in container:
+ return container['Name']
+ # ps
+ shortest_name = min(container['Names'], key=lambda n: len(n.split('/')))
+ return shortest_name.split('/')[-1]
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/errors.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/errors.py b/env2/lib/python2.7/site-packages/compose/errors.py
new file mode 100644
index 0000000..9f68760
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/errors.py
@@ -0,0 +1,7 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+
+class OperationFailedError(Exception):
+ def __init__(self, reason):
+ self.msg = reason
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/network.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/network.py b/env2/lib/python2.7/site-packages/compose/network.py
new file mode 100644
index 0000000..affba7c
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/network.py
@@ -0,0 +1,190 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+import logging
+
+from docker.errors import NotFound
+from docker.utils import create_ipam_config
+from docker.utils import create_ipam_pool
+
+from .config import ConfigurationError
+
+
+log = logging.getLogger(__name__)
+
+
+class Network(object):
+ def __init__(self, client, project, name, driver=None, driver_opts=None,
+ ipam=None, external_name=None):
+ self.client = client
+ self.project = project
+ self.name = name
+ self.driver = driver
+ self.driver_opts = driver_opts
+ self.ipam = create_ipam_config_from_dict(ipam)
+ self.external_name = external_name
+
+ def ensure(self):
+ if self.external_name:
+ try:
+ self.inspect()
+ log.debug(
+ 'Network {0} declared as external. No new '
+ 'network will be created.'.format(self.name)
+ )
+ except NotFound:
+ raise ConfigurationError(
+ 'Network {name} declared as external, but could'
+ ' not be found. Please create the network manually'
+ ' using `{command} {name}` and try again.'.format(
+ name=self.external_name,
+ command='docker network create'
+ )
+ )
+ return
+
+ try:
+ data = self.inspect()
+ if self.driver and data['Driver'] != self.driver:
+ raise ConfigurationError(
+ 'Network "{}" needs to be recreated - driver has changed'
+ .format(self.full_name))
+ if data['Options'] != (self.driver_opts or {}):
+ raise ConfigurationError(
+ 'Network "{}" needs to be recreated - options have changed'
+ .format(self.full_name))
+ except NotFound:
+ driver_name = 'the default driver'
+ if self.driver:
+ driver_name = 'driver "{}"'.format(self.driver)
+
+ log.info(
+ 'Creating network "{}" with {}'
+ .format(self.full_name, driver_name)
+ )
+
+ self.client.create_network(
+ name=self.full_name,
+ driver=self.driver,
+ options=self.driver_opts,
+ ipam=self.ipam,
+ )
+
+ def remove(self):
+ if self.external_name:
+ log.info("Network %s is external, skipping", self.full_name)
+ return
+
+ log.info("Removing network {}".format(self.full_name))
+ self.client.remove_network(self.full_name)
+
+ def inspect(self):
+ return self.client.inspect_network(self.full_name)
+
+ @property
+ def full_name(self):
+ if self.external_name:
+ return self.external_name
+ return '{0}_{1}'.format(self.project, self.name)
+
+
+def create_ipam_config_from_dict(ipam_dict):
+ if not ipam_dict:
+ return None
+
+ return create_ipam_config(
+ driver=ipam_dict.get('driver'),
+ pool_configs=[
+ create_ipam_pool(
+ subnet=config.get('subnet'),
+ iprange=config.get('ip_range'),
+ gateway=config.get('gateway'),
+ aux_addresses=config.get('aux_addresses'),
+ )
+ for config in ipam_dict.get('config', [])
+ ],
+ )
+
+
+def build_networks(name, config_data, client):
+ network_config = config_data.networks or {}
+ networks = {
+ network_name: Network(
+ client=client, project=name, name=network_name,
+ driver=data.get('driver'),
+ driver_opts=data.get('driver_opts'),
+ ipam=data.get('ipam'),
+ external_name=data.get('external_name'),
+ )
+ for network_name, data in network_config.items()
+ }
+
+ if 'default' not in networks:
+ networks['default'] = Network(client, name, 'default')
+
+ return networks
+
+
+class ProjectNetworks(object):
+
+ def __init__(self, networks, use_networking):
+ self.networks = networks or {}
+ self.use_networking = use_networking
+
+ @classmethod
+ def from_services(cls, services, networks, use_networking):
+ service_networks = {
+ network: networks.get(network)
+ for service in services
+ for network in get_network_names_for_service(service)
+ }
+ unused = set(networks) - set(service_networks) - {'default'}
+ if unused:
+ log.warn(
+ "Some networks were defined but are not used by any service: "
+ "{}".format(", ".join(unused)))
+ return cls(service_networks, use_networking)
+
+ def remove(self):
+ if not self.use_networking:
+ return
+ for network in self.networks.values():
+ try:
+ network.remove()
+ except NotFound:
+ log.warn("Network %s not found.", network.full_name)
+
+ def initialize(self):
+ if not self.use_networking:
+ return
+
+ for network in self.networks.values():
+ network.ensure()
+
+
+def get_network_defs_for_service(service_dict):
+ if 'network_mode' in service_dict:
+ return {}
+ networks = service_dict.get('networks', {'default': None})
+ return dict(
+ (net, (config or {}))
+ for net, config in networks.items()
+ )
+
+
+def get_network_names_for_service(service_dict):
+ return get_network_defs_for_service(service_dict).keys()
+
+
+def get_networks(service_dict, network_definitions):
+ networks = {}
+ for name, netdef in get_network_defs_for_service(service_dict).items():
+ network = network_definitions.get(name)
+ if network:
+ networks[network.full_name] = netdef
+ else:
+ raise ConfigurationError(
+ 'Service "{}" uses an undefined network "{}"'
+ .format(service_dict['name'], name))
+
+ return networks
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/parallel.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/parallel.py b/env2/lib/python2.7/site-packages/compose/parallel.py
new file mode 100644
index 0000000..7ac66b3
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/parallel.py
@@ -0,0 +1,254 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+import logging
+import operator
+import sys
+from threading import Thread
+
+from docker.errors import APIError
+from six.moves import _thread as thread
+from six.moves.queue import Empty
+from six.moves.queue import Queue
+
+from compose.cli.signals import ShutdownException
+from compose.errors import OperationFailedError
+from compose.utils import get_output_stream
+
+
+log = logging.getLogger(__name__)
+
+STOP = object()
+
+
+def parallel_execute(objects, func, get_name, msg, get_deps=None):
+ """Runs func on objects in parallel while ensuring that func is
+ ran on object only after it is ran on all its dependencies.
+
+ get_deps called on object must return a collection with its dependencies.
+ get_name called on object must return its name.
+ """
+ objects = list(objects)
+ stream = get_output_stream(sys.stderr)
+
+ writer = ParallelStreamWriter(stream, msg)
+ for obj in objects:
+ writer.initialize(get_name(obj))
+
+ events = parallel_execute_iter(objects, func, get_deps)
+
+ errors = {}
+ results = []
+ error_to_reraise = None
+
+ for obj, result, exception in events:
+ if exception is None:
+ writer.write(get_name(obj), 'done')
+ results.append(result)
+ elif isinstance(exception, APIError):
+ errors[get_name(obj)] = exception.explanation
+ writer.write(get_name(obj), 'error')
+ elif isinstance(exception, OperationFailedError):
+ errors[get_name(obj)] = exception.msg
+ writer.write(get_name(obj), 'error')
+ elif isinstance(exception, UpstreamError):
+ writer.write(get_name(obj), 'error')
+ else:
+ errors[get_name(obj)] = exception
+ error_to_reraise = exception
+
+ for obj_name, error in errors.items():
+ stream.write("\nERROR: for {} {}\n".format(obj_name, error))
+
+ if error_to_reraise:
+ raise error_to_reraise
+
+ return results, errors
+
+
+def _no_deps(x):
+ return []
+
+
+class State(object):
+ """
+ Holds the state of a partially-complete parallel operation.
+
+ state.started: objects being processed
+ state.finished: objects which have been processed
+ state.failed: objects which either failed or whose dependencies failed
+ """
+ def __init__(self, objects):
+ self.objects = objects
+
+ self.started = set()
+ self.finished = set()
+ self.failed = set()
+
+ def is_done(self):
+ return len(self.finished) + len(self.failed) >= len(self.objects)
+
+ def pending(self):
+ return set(self.objects) - self.started - self.finished - self.failed
+
+
+def parallel_execute_iter(objects, func, get_deps):
+ """
+ Runs func on objects in parallel while ensuring that func is
+ ran on object only after it is ran on all its dependencies.
+
+ Returns an iterator of tuples which look like:
+
+ # if func returned normally when run on object
+ (object, result, None)
+
+ # if func raised an exception when run on object
+ (object, None, exception)
+
+ # if func raised an exception when run on one of object's dependencies
+ (object, None, UpstreamError())
+ """
+ if get_deps is None:
+ get_deps = _no_deps
+
+ results = Queue()
+ state = State(objects)
+
+ while True:
+ feed_queue(objects, func, get_deps, results, state)
+
+ try:
+ event = results.get(timeout=0.1)
+ except Empty:
+ continue
+ # See https://github.com/docker/compose/issues/189
+ except thread.error:
+ raise ShutdownException()
+
+ if event is STOP:
+ break
+
+ obj, _, exception = event
+ if exception is None:
+ log.debug('Finished processing: {}'.format(obj))
+ state.finished.add(obj)
+ else:
+ log.debug('Failed: {}'.format(obj))
+ state.failed.add(obj)
+
+ yield event
+
+
+def producer(obj, func, results):
+ """
+ The entry point for a producer thread which runs func on a single object.
+ Places a tuple on the results queue once func has either returned or raised.
+ """
+ try:
+ result = func(obj)
+ results.put((obj, result, None))
+ except Exception as e:
+ results.put((obj, None, e))
+
+
+def feed_queue(objects, func, get_deps, results, state):
+ """
+ Starts producer threads for any objects which are ready to be processed
+ (i.e. they have no dependencies which haven't been successfully processed).
+
+ Shortcuts any objects whose dependencies have failed and places an
+ (object, None, UpstreamError()) tuple on the results queue.
+ """
+ pending = state.pending()
+ log.debug('Pending: {}'.format(pending))
+
+ for obj in pending:
+ deps = get_deps(obj)
+
+ if any(dep in state.failed for dep in deps):
+ log.debug('{} has upstream errors - not processing'.format(obj))
+ results.put((obj, None, UpstreamError()))
+ state.failed.add(obj)
+ elif all(
+ dep not in objects or dep in state.finished
+ for dep in deps
+ ):
+ log.debug('Starting producer thread for {}'.format(obj))
+ t = Thread(target=producer, args=(obj, func, results))
+ t.daemon = True
+ t.start()
+ state.started.add(obj)
+
+ if state.is_done():
+ results.put(STOP)
+
+
+class UpstreamError(Exception):
+ pass
+
+
+class ParallelStreamWriter(object):
+ """Write out messages for operations happening in parallel.
+
+ Each operation has it's own line, and ANSI code characters are used
+ to jump to the correct line, and write over the line.
+ """
+
+ def __init__(self, stream, msg):
+ self.stream = stream
+ self.msg = msg
+ self.lines = []
+
+ def initialize(self, obj_index):
+ if self.msg is None:
+ return
+ self.lines.append(obj_index)
+ self.stream.write("{} {} ... \r\n".format(self.msg, obj_index))
+ self.stream.flush()
+
+ def write(self, obj_index, status):
+ if self.msg is None:
+ return
+ position = self.lines.index(obj_index)
+ diff = len(self.lines) - position
+ # move up
+ self.stream.write("%c[%dA" % (27, diff))
+ # erase
+ self.stream.write("%c[2K\r" % 27)
+ self.stream.write("{} {} ... {}\r".format(self.msg, obj_index, status))
+ # move back down
+ self.stream.write("%c[%dB" % (27, diff))
+ self.stream.flush()
+
+
+def parallel_operation(containers, operation, options, message):
+ parallel_execute(
+ containers,
+ operator.methodcaller(operation, **options),
+ operator.attrgetter('name'),
+ message)
+
+
+def parallel_remove(containers, options):
+ stopped_containers = [c for c in containers if not c.is_running]
+ parallel_operation(stopped_containers, 'remove', options, 'Removing')
+
+
+def parallel_start(containers, options):
+ parallel_operation(containers, 'start', options, 'Starting')
+
+
+def parallel_pause(containers, options):
+ parallel_operation(containers, 'pause', options, 'Pausing')
+
+
+def parallel_unpause(containers, options):
+ parallel_operation(containers, 'unpause', options, 'Unpausing')
+
+
+def parallel_kill(containers, options):
+ parallel_operation(containers, 'kill', options, 'Killing')
+
+
+def parallel_restart(containers, options):
+ parallel_operation(containers, 'restart', options, 'Restarting')
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/1ca22406/env2/lib/python2.7/site-packages/compose/progress_stream.py
----------------------------------------------------------------------
diff --git a/env2/lib/python2.7/site-packages/compose/progress_stream.py b/env2/lib/python2.7/site-packages/compose/progress_stream.py
new file mode 100644
index 0000000..a0f5601
--- /dev/null
+++ b/env2/lib/python2.7/site-packages/compose/progress_stream.py
@@ -0,0 +1,112 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+from compose import utils
+
+
+class StreamOutputError(Exception):
+ pass
+
+
+def stream_output(output, stream):
+ is_terminal = hasattr(stream, 'isatty') and stream.isatty()
+ stream = utils.get_output_stream(stream)
+ all_events = []
+ lines = {}
+ diff = 0
+
+ for event in utils.json_stream(output):
+ all_events.append(event)
+ is_progress_event = 'progress' in event or 'progressDetail' in event
+
+ if not is_progress_event:
+ print_output_event(event, stream, is_terminal)
+ stream.flush()
+ continue
+
+ if not is_terminal:
+ continue
+
+ # if it's a progress event and we have a terminal, then display the progress bars
+ image_id = event.get('id')
+ if not image_id:
+ continue
+
+ if image_id in lines:
+ diff = len(lines) - lines[image_id]
+ else:
+ lines[image_id] = len(lines)
+ stream.write("\n")
+ diff = 0
+
+ # move cursor up `diff` rows
+ stream.write("%c[%dA" % (27, diff))
+
+ print_output_event(event, stream, is_terminal)
+
+ if 'id' in event:
+ # move cursor back down
+ stream.write("%c[%dB" % (27, diff))
+
+ stream.flush()
+
+ return all_events
+
+
+def print_output_event(event, stream, is_terminal):
+ if 'errorDetail' in event:
+ raise StreamOutputError(event['errorDetail']['message'])
+
+ terminator = ''
+
+ if is_terminal and 'stream' not in event:
+ # erase current line
+ stream.write("%c[2K\r" % 27)
+ terminator = "\r"
+ elif 'progressDetail' in event:
+ return
+
+ if 'time' in event:
+ stream.write("[%s] " % event['time'])
+
+ if 'id' in event:
+ stream.write("%s: " % event['id'])
+
+ if 'from' in event:
+ stream.write("(from %s) " % event['from'])
+
+ status = event.get('status', '')
+
+ if 'progress' in event:
+ stream.write("%s %s%s" % (status, event['progress'], terminator))
+ elif 'progressDetail' in event:
+ detail = event['progressDetail']
+ total = detail.get('total')
+ if 'current' in detail and total:
+ percentage = float(detail['current']) / float(total) * 100
+ stream.write('%s (%.1f%%)%s' % (status, percentage, terminator))
+ else:
+ stream.write('%s%s' % (status, terminator))
+ elif 'stream' in event:
+ stream.write("%s%s" % (event['stream'], terminator))
+ else:
+ stream.write("%s%s\n" % (status, terminator))
+
+
+def get_digest_from_pull(events):
+ for event in events:
+ status = event.get('status')
+ if not status or 'Digest' not in status:
+ continue
+
+ _, digest = status.split(':', 1)
+ return digest.strip()
+ return None
+
+
+def get_digest_from_push(events):
+ for event in events:
+ digest = event.get('aux', {}).get('Digest')
+ if digest:
+ return digest
+ return None