Write out a json file containing container startup info and create tool to use it.
authorIan Main <imain@redhat.com>
Thu, 9 Feb 2017 21:23:59 +0000 (16:23 -0500)
committerIan Main <imain@redhat.com>
Wed, 22 Feb 2017 18:29:51 +0000 (18:29 +0000)
This adds a bit to the post.yaml for docker to write out a json file
containing all the information on how we are start docker containers
(thanks Dan!).  I've then written a script that parses this that can
be used to execute docker run commands in various ways for debugging
purposes.

Change-Id: I36d66b42d1ac5030db8841820d4fc512a71d1285
Co-Authored-by: Dan Prince <dprince@redhat.com>
docker/docker-toool [new file with mode: 0755]
docker/post.j2.yaml

diff --git a/docker/docker-toool b/docker/docker-toool
new file mode 100755 (executable)
index 0000000..36aba4a
--- /dev/null
@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+#    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.
+
+import argparse
+import os
+import shutil
+import sys
+import json
+
+docker_cmd = '/bin/docker'
+
+# Tool to start docker containers as configured via
+# tripleo-heat-templates.
+#
+# This tool reads data from a json file generated from heat when the
+# TripleO stack is run.  All the configuration data used to start the
+# containerized services is in this file.
+#
+# By default this tool lists all the containers that are started and
+# their start order.
+#
+# If you wish to see the command line used to start a given container,
+# specify it by name using the --container argument.  --run can then be
+# used with this to actually execute docker to run the container.\n
+#
+# Other options listed allow you to modify this command line for
+# debugging purposes.  For example:
+#
+# docker-toool -c swift-proxy -r -e /bin/bash -u root -i -n test
+#
+# will run the swift proxy container as user root, executing /bin/bash,
+#
+# named 'test', and will run interactively (eg -ti).
+
+
+def parse_opts(argv):
+    parser = argparse.ArgumentParser("Tool to start docker containers via "
+                                     "TripleO configurations")
+    parser.add_argument('-f', '--config',
+                        help="""File to use as docker startup configuration data.""",
+                        default='/var/lib/docker-container-startup-configs.json')
+    parser.add_argument('-r', '--run',
+                        action='store_true',
+                        help="""Run the container as specified with --container.""",
+                        default=False)
+    parser.add_argument('-e', '--command',
+                        help="""Override the command used to run the container.""",
+                        default='')
+    parser.add_argument('-c', '--container',
+                        help="""Specify a container to run or show the command for.""",
+                        default='')
+    parser.add_argument('-u', '--user',
+                        help="""User to run container as.""",
+                        default='')
+    parser.add_argument('-n', '--name',
+                        help="""Name of container.""",
+                        default='')
+    parser.add_argument('-i', '--interactive',
+                        action='store_true',
+                        help="""Start docker container interactively (-ti).""",
+                        default=False)
+    opts = parser.parse_args(argv[1:])
+
+    return opts
+
+def docker_arg_map(key, value):
+    value = str(value).encode('ascii', 'ignore')
+    return {
+        'environment': "--env=%s" % value,
+        # 'image': value,
+        'net': "--net=%s" % value,
+        'pid': "--pid=%s" % value,
+        'privileged': "--privileged=%s" % value.lower(),
+        #'restart': "--restart=%s" % "false",
+        'user': "--user=%s" % value,
+        'volumes': "--volume=%s" % value,
+        'volumes_from': "--volumes-from=%s" % value,
+    }.get(key, None)
+
+def run_docker_container(opts, container_name):
+    container_found = False
+
+    with open(opts.config) as f:
+        json_data = json.load(f)
+
+    for step in (json_data or []):
+        if step is None:
+            continue
+        for container in (json_data[step] or []):
+            if container == container_name:
+                print('container found: %s' % container)
+                container_found = True
+                # A few positional arguments:
+                command = ''
+                image = ''
+
+                cmd = [
+                    docker_cmd,
+                    'run',
+                    '--name',
+                    opts.name or container
+                ]
+                for container_data in (json_data[step][container] or []):
+                    if container_data == "environment":
+                        for env in (json_data[step][container][container_data] or []):
+                            arg = docker_arg_map("environment", env)
+                            if arg:
+                                cmd.append(arg)
+                    elif container_data == "volumes":
+                        for volume in (json_data[step][container][container_data] or []):
+                            arg = docker_arg_map("volumes", volume)
+                            if arg:
+                                cmd.append(arg)
+                    elif container_data == "volumes_from":
+                        for volume in (json_data[step][container][container_data] or []):
+                            arg = docker_arg_map("volumes_from", volume)
+                            if arg:
+                                cmd.append(arg)
+                    elif container_data == 'command':
+                        command = json_data[step][container][container_data]
+                    elif container_data == 'image':
+                        image = json_data[step][container][container_data]
+                    else:
+                        # Only add a restart if we're not interactive
+                        if container_data == 'restart':
+                            if opts.interactive:
+                                continue
+                        if container_data == 'user':
+                            if opts.user:
+                                continue
+                        arg = docker_arg_map(container_data,
+                                json_data[step][container][container_data])
+                        if arg:
+                            cmd.append(arg)
+
+                if opts.user:
+                    cmd.append('--user')
+                    cmd.append(opts.user)
+                if opts.interactive:
+                    cmd.append('-ti')
+                    # May as well remove it when we're done too
+                    cmd.append('--rm')
+                cmd.append(image)
+                if opts.command:
+                    cmd.append(opts.command)
+                elif command:
+                    cmd.extend(command)
+
+                print ' '.join(cmd)
+
+                if opts.run:
+                    os.execl(docker_cmd, *cmd)
+
+    if not container_found:
+        print("Container '%s' not found!" % container_name)
+
+def list_docker_containers(opts):
+    print opts
+    with open(opts.config) as f:
+        json_data = json.load(f)
+
+    for step in (json_data or []):
+        if step is None:
+            continue
+        print step
+        for container in (json_data[step] or []):
+            print('\tcontainer: %s' % container)
+            for container_data in (json_data[step][container] or []):
+                #print('\t\tcontainer_data: %s' % container_data)
+                if container_data == "start_order":
+                    print('\t\tstart_order: %s' % json_data[step][container][container_data])
+
+opts = parse_opts(sys.argv)
+
+if opts.container:
+    run_docker_container(opts, opts.container)
+else:
+    list_docker_containers(opts)
+
index 3473f4c..ef83966 100644 (file)
@@ -187,6 +187,24 @@ resources:
             docker_config: {get_param: [role_data, {{role.name}}, docker_config]}
             docker_image: {get_param: [role_data, {{role.name}}, docker_image]}
 
+  # Here we are dumping all the docker container startup configuration data
+  # so that we can have access to how they are started outside of heat
+  # and docker-cmd.  This lets us create command line tools to start and
+  # test these containers.
+  {{role.name}}DockerConfigJsonStartupData:
+    type: OS::Heat::StructuredConfig
+    properties:
+      group: json-file
+      config:
+        /var/lib/docker-container-startup-configs.json:
+          {get_attr: [{{role.name}}DockerConfig, value]}
+
+  {{role.name}}DockerConfigJsonStartupDataDeployment:
+    type: OS::Heat::SoftwareDeploymentGroup
+    properties:
+      config: {get_resource: {{role.name}}DockerConfigJsonStartupData}
+      servers: {get_param: [servers, {{role.name}}]}
+
   {{role.name}}KollaJsonConfig:
     type: OS::Heat::StructuredConfig
     properties: