components/docker/files/docker-support
changeset 7177 86d14f182e82
parent 7119 2f82d964b8be
child 7794 4953ab4958ad
--- a/components/docker/files/docker-support	Fri Oct 28 16:40:06 2016 -0700
+++ b/components/docker/files/docker-support	Fri Oct 28 18:45:06 2016 -0700
@@ -4,18 +4,73 @@
 #
 
 import argparse
+import httplib
+import json
+from multiprocessing.dummy import Pool as ThreadPool 
 import os
 import shutil
+import socket
 from subprocess import Popen, PIPE
 import sys
 import tempfile
 
+DOCKER_SOCK = "/var/run/docker/docker.sock"
 ROOTFS_ARCHIVE  = "rootfs.tar.gz"
 DOCKERFILE = """FROM scratch
 ADD %(archive)s /
 CMD /bin/bash
 """
 
+
+class HTTPConnectionSocket(httplib.HTTPConnection):
+    """HTTPConnection for local UNIX sockets."""
+    def __init__(self, sock_path):
+        httplib.HTTPConnection.__init__(self, 'localhost')
+        self.sock_path = sock_path
+
+    def connect(self):
+        # superclass uses self.sock, he'll handle cleanup
+        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        self.sock.connect(self.sock_path)
+
+
+def do_api_get(url):
+    try:
+        con = HTTPConnectionSocket(DOCKER_SOCK)
+        con.request("GET", url)
+        resp = con.getresponse()
+    except httplib.HTTPException as err:
+        raise RuntimeError("unable to call API: %s" % err)
+
+    # expect standard success status
+    if resp.status != 200:
+        raise RuntimeError("Unable to query Docker API: status [%s] "
+            "reason [%s]" % (resp.status, resp.reason))
+
+    try:
+        data = json.loads(resp.read())
+    except Exception as err:
+        raise RuntimeError("unexpected error parsing JSON: %s" % err)
+
+    con.close()
+    return data
+
+
+def do_api_post(url):
+    try:
+        con = HTTPConnectionSocket(DOCKER_SOCK)
+        con.request("POST", url)
+        resp = con.getresponse()
+    except httplib.HTTPException as err:
+        raise RuntimeError("unable to call API: %s" % err)
+
+    # expect success and no content
+    if resp.status != 204:
+        raise RuntimeError("Unable to query Docker API: status [%s] "
+            "reason [%s]" % (resp.status, resp.reason))
+    con.close()
+
+
 class DockerSupportCmd(object):
     def __init__(self, cmd, verbose=False):
         self.cmd = cmd
@@ -35,10 +90,11 @@
 
 def docker_is_online():
     try:
-        return DockerSupportCmd(['/usr/bin/svcs', '-Ho', 'state',
-            'docker']).run().strip() == 'online'
+        status = DockerSupportCmd(['/usr/bin/svcs', '-Ho', 'state',
+            'docker']).run().strip()
+        return status.startswith('online') or status.startswith('degraded')
     except Exception as err:
-        raise RuntimeError("Unable to determine version: %s" % err)
+        raise RuntimeError("Unable to determine service status: %s" % err)
 
 
 def get_os_version():
@@ -108,6 +164,36 @@
         shutil.rmtree(temp_dir)
 
 
+def get_running_container_ids():
+    try:
+        return [container['Id'] for container in
+            do_api_get("http:/containers/json")]
+    except RuntimeError as e:
+        print "unable to query api for container list: %s" % e
+
+
+def kill_container(cid):
+    try:
+        do_api_post("http:/containers/%s/kill" % cid)
+        print "shutdown container [%s]" % cid
+    except RuntimeError as e:
+        print "unable to kill container [%s]: %s" % (cid, e)
+
+
+def shutdown_containers(args):
+    print "Shutting down all running container instances..."
+    if not docker_is_online():
+        return
+
+    pool = ThreadPool(64)
+    pool.map(kill_container, get_running_container_ids())
+    pool.close()
+    pool.join()
+
+    if get_running_container_ids():
+        print "NOTE: unable to gracefully shutdown all containers"
+
+
 def build_parser():
     parser_main = argparse.ArgumentParser()
     parser_main.add_argument("-v", "--version", action="version",
@@ -122,8 +208,12 @@
         help="use development build options for the package image")
     parser_create.add_argument("-p", "--profile",
         help="TEMPORARY: optional syconfig profile")
+    parser_create.set_defaults(func=create_base_image)
 
-    parser_create.set_defaults(func=create_base_image)
+    parser_shutdown = subparsers.add_parser("shutdown-containers",
+        help="gracefully kill all running container instances",
+        usage=argparse.SUPPRESS)
+    parser_shutdown.set_defaults(func=shutdown_containers)
 
     return parser_main