summaryrefslogtreecommitdiffstats
path: root/python-dockering/dockering/config_building.py
diff options
context:
space:
mode:
authorTommy Carpenter <tommy@research.att.com>2017-08-23 11:21:44 -0400
committerTommy Carpenter <tommy@research.att.com>2017-08-23 12:32:45 -0400
commit81b9ed7a3af1032caa86f316a21e9b2912bb1271 (patch)
treefe3bb349f501619d3b4df4d4dbb07b9d72adc824 /python-dockering/dockering/config_building.py
parent280af47e5342de6bb4657ad7b85adcc9395ace20 (diff)
Intiial utils push to ONAP
Issue-Id: DCAEGEN2-80 Change-Id: I1dc8f2d384b0af346fccb86e6767b7e3ca484078 Signed-off-by: Tommy Carpenter <tommy@research.att.com>
Diffstat (limited to 'python-dockering/dockering/config_building.py')
-rw-r--r--python-dockering/dockering/config_building.py269
1 files changed, 269 insertions, 0 deletions
diff --git a/python-dockering/dockering/config_building.py b/python-dockering/dockering/config_building.py
new file mode 100644
index 0000000..d8e3c84
--- /dev/null
+++ b/python-dockering/dockering/config_building.py
@@ -0,0 +1,269 @@
+# org.onap.dcae
+# ================================================================================
+# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+#
+# ECOMP is a trademark and service mark of AT&T Intellectual Property.
+
+"""
+Abstraction in Docker container configuration
+"""
+from dockering import utils
+from dockering.exceptions import DockerConstructionError
+
+
+#
+# Methods to build container envs
+#
+
+def create_envs_healthcheck(docker_config, default_interval="15s",
+ default_timeout="1s"):
+ """Extract health check environment variables for Docker containers
+
+ Parameters
+ ----------
+ docker_config: dict where there's an entry called "healthcheck"
+
+ Returns
+ -------
+ dict of Docker envs for healthcheck
+ """
+ # TODO: This has been shamefully lifted from the dcae-cli and should probably
+ # shared as a library. The unit tests are there. The difference is that
+ # there are defaults that are passed in here but the defaults should really
+ # come from the component spec definition. The issue is that nothing forwards
+ # those defaults.
+
+ envs = dict()
+ hc = docker_config["healthcheck"]
+
+ # NOTE: For the multiple port, schema scenario, you can explicitly set port
+ # to schema. For example if image EXPOSE 8080, SERVICE_8080_CHECK_HTTP works.
+ # https://github.com/gliderlabs/registrator/issues/311
+
+ if hc["type"] == "http":
+ envs["SERVICE_CHECK_HTTP"] = hc["endpoint"]
+ elif hc["type"] == "https":
+ # WATCH: HTTPS health checks don't work. Seems like Registrator bug.
+ # Submitted issue https://github.com/gliderlabs/registrator/issues/516
+ envs["SERVICE_CHECK_HTTPS"] = hc["endpoint"]
+ utils.logger.warn("Https-based health checks may not work because Registrator issue #516")
+ elif hc["type"] == "script":
+ envs["SERVICE_CHECK_SCRIPT"] = hc["script"]
+ elif hc["type"] == "docker":
+ # Note this is only supported in the AT&T open source version of registrator
+ envs["SERVICE_CHECK_DOCKER_SCRIPT"] = hc["script"]
+ else:
+ # You should never get here but not having an else block feels weird
+ raise DockerConstructionError("Unexpected health check type: {0}".format(hc["type"]))
+
+ envs["SERVICE_CHECK_INTERVAL"] = hc.get("interval", default_interval)
+ envs["SERVICE_CHECK_TIMEOUT"] = hc.get("timeout", default_timeout)
+
+ return envs
+
+
+def create_envs(service_component_name, *envs):
+ """Merge all environment variables maps
+
+ Creates a complete environment variables map that is to be used for creating
+ the container.
+
+ Args:
+ -----
+ envs: Arbitrary list of dicts where each dict is of the structure:
+
+ {
+ <environment variable name>: <environment variable value>,
+ <environment variable name>: <environment variable value>,
+ ...
+ }
+
+ Returns:
+ --------
+ Dict of all environment variable name to environment variable value
+ """
+ master_envs = { "HOSTNAME": service_component_name,
+ # For Registrator to register with generated name and not the
+ # image name
+ "SERVICE_NAME": service_component_name }
+ for envs_map in envs:
+ master_envs.update(envs_map)
+ return master_envs
+
+
+#
+# Methods for volume bindings
+#
+
+def _parse_volumes_param(volumes):
+ """Parse volumes details for Docker containers from blueprint
+
+ Takes in a list of dicts that contains Docker volume info and
+ transforms them into docker-py compliant (unflattened) data structures.
+ Look for the `volumes` parameters under the `run` method on
+ [this page](https://docker-py.readthedocs.io/en/stable/containers.html)
+
+ Args:
+ volumes (list): List of
+
+ {
+ "host": {
+ "path": <target path on host>
+ },
+ "container": {
+ "bind": <target path in container>,
+ "mode": <read/write>
+ }
+ }
+
+ Returns:
+ dict of the form
+
+ {
+ <target path on host>: {
+ "bind": <target path in container>,
+ "mode": <read/write>
+ }
+ }
+
+ if volumes is None then returns None
+ """
+ if volumes:
+ return dict([ (vol["host"]["path"], vol["container"]) for vol in volumes ])
+ else:
+ return None
+
+
+#
+# Utility methods used to help build the inputs to create the host_config
+#
+
+def add_host_config_params_volumes(volumes=None, host_config_params=None):
+ """Add volumes input params
+
+ Args:
+ -----
+ volumes (list): List of
+
+ {
+ "host": {
+ "path": <target path on host>
+ },
+ "container": {
+ "bind": <target path in container>,
+ "mode": <read/write>
+ }
+ }
+
+ host_config_params (dict): Target dict to accumulate host config inputs
+
+ Returns:
+ --------
+ Updated host_config_params
+ """
+# TODO: USE parse_volumes_param here!
+ if host_config_params == None:
+ host_config_params = {}
+
+ host_config_params["binds"] = _parse_volumes_param(volumes)
+ return host_config_params
+
+def add_host_config_params_ports(ports=None, host_config_params=None):
+ """Add ports input params
+
+ Args:
+ -----
+ ports (list): Each port mapping entry is of the form
+
+ "<container ports>:<host port>"
+
+ host_config_params (dict): Target dict to accumulate host config inputs
+
+ Returns:
+ --------
+ Updated host_config_params
+ """
+ if host_config_params == None:
+ host_config_params = {}
+
+ if ports:
+ ports = [ port.split(":") for port in ports ]
+ port_bindings = { port[0]: { "HostPort": port[1] } for port in ports }
+ host_config_params["port_bindings"] = port_bindings
+ host_config_params["publish_all_ports"] = False
+ else:
+ host_config_params["publish_all_ports"] = True
+
+ return host_config_params
+
+def add_host_config_params_dns(docker_host, host_config_params=None):
+ """Add dns input params
+
+ This is not a generic implementation. This method will setup dns with the
+ expectation that a local consul agent is running on the docker host and will
+ service the dns requests.
+
+ Args:
+ -----
+ docker_host (string): Docker host ip address which will be used as the dns server
+ host_config_params (dict): Target dict to accumulate host config inputs
+
+ Returns:
+ --------
+ Updated host_config_params
+ """
+ if host_config_params == None:
+ host_config_params = {}
+
+ host_config_params["dns"] = [docker_host]
+ host_config_params["dns_search"] = ["service.consul"]
+ host_config_params["extra_hosts"] = { "consul": docker_host }
+ return host_config_params
+
+
+def create_container_config(client, image, envs, host_config_params, tty=False):
+ """Create docker container config
+
+ Args:
+ -----
+ envs (dict): dict of environment variables to pass into the docker containers.
+ Gets passed into docker-py.create_container call
+ host_config_params (dict): Dict of input parameters to the docker-py
+ "create_host_config" method call
+ """
+ # This is the 1.10.6 approach to binding volumes
+ # http://docker-py.readthedocs.io/en/1.10.6/volumes.html
+ volumes = host_config_params.get("bind", None)
+ target_volumes = [ target["bind"] for target in volumes.values() ] \
+ if volumes else None
+
+ host_config = client.create_host_config(**host_config_params)
+
+ if "port_bindings" in host_config_params:
+ # TODO: Use six for creating the list of ports - six.iterkeys
+ ports = host_config_params["port_bindings"].keys()
+ else:
+ ports = None
+
+ command = "" # This is required...
+ config = client.create_container_config(image, command, detach=True, tty=tty,
+ host_config=host_config, ports=ports,
+ environment=envs, volumes=target_volumes)
+
+ utils.logger.info("Docker container config: {0}".format(config))
+
+ return config
+