10343: Merge branch 'master' into 10343-dockercleaner-config
authorTom Clegg <tom@curoverse.com>
Mon, 31 Oct 2016 20:08:01 +0000 (16:08 -0400)
committerTom Clegg <tom@curoverse.com>
Mon, 31 Oct 2016 20:08:01 +0000 (16:08 -0400)
build/go-python-package-scripts/postinst
build/go-python-package-scripts/prerm
doc/_includes/_install_docker_cleaner.liquid
services/dockercleaner/arvados-docker-cleaner.service
services/dockercleaner/arvados_docker/cleaner.py
services/dockercleaner/setup.py
services/dockercleaner/tests/test_cleaner.py

index 051c8bd98bd379cf29752f08ea1d88284706d60e..729855a8806faa938bda6e9d3a95a3159eca08b5 100755 (executable)
@@ -2,39 +2,61 @@
 
 set -e
 
-# NOTE: This package name detection will only work on Debian.
-# If this postinst script ever starts doing work on Red Hat,
-# we'll need to adapt this code accordingly.
-script="$(basename "${0}")"
-pkg="${script%.postinst}"
-systemd_unit="${pkg}.service"
+if [ "%{name}" != "%\{name\}" ]; then
+    # Red Hat ("%{...}" is interpolated at package build time)
+    pkg="%{name}"
+    pkgtype=rpm
+    prefix="${RPM_INSTALL_PREFIX}"
+else
+    # Debian
+    script="$(basename "${0}")"
+    pkg="${script%.postinst}"
+    pkgtype=deb
+    prefix=/usr
+fi
 
-case "${1}" in
-    configure)
-        if [ -d /lib/systemd/system ]
-        then
-            # Python packages put all data files in /usr, so we copy
-            # them to /lib at install time.
-            py_unit="/usr/share/doc/${pkg}/${pkg}.service"
-            if [ -e "${py_unit}" ]
-            then
-                cp "${py_unit}" /lib/systemd/system/
+case "${pkgtype}-${1}" in
+    deb-configure | rpm-1)
+        dest_dir="/lib/systemd/system"
+        if ! [ -d "${dest_dir}" ]; then
+            exit 0
+        fi
+
+        # Find the unit file we need to install.
+        unit_file="${pkg}.service"
+        for dir in \
+            "${prefix}/share/doc/${pkg}" \
+            "${dest_dir}"; do
+            if [ -e "${dir}/${unit_file}" ]; then
+                src_dir="${dir}"
+                break
             fi
+        done
+        if [ -z "${src_dir}" ]; then
+            echo >&2 "WARNING: postinst script did not find ${unit_file} anywhere."
+            exit 0
+        fi
+
+        # Install/update the unit file if necessary.
+        if [ "${src_dir}" != "${dest_dir}" ]; then
+            cp "${src_dir}/${unit_file}" "${dest_dir}/" || exit 0
         fi
 
+        # Enable service, and make sure systemd re-reads the unit
+        # file, in case we changed it.
         if [ -e /run/systemd/system ]; then
-            eval "$(systemctl -p UnitFileState show "${systemd_unit}")"
+            systemctl daemon-reload || true
+            eval "$(systemctl -p UnitFileState show "${pkg}")"
             case "${UnitFileState}" in
                 disabled)
                     # Failing to enable or start the service is not a
                     # package error, so don't let errors here
                     # propagate up.
-                    systemctl enable "${systemd_unit}" || true
-                    systemctl start "${systemd_unit}" || true
+                    systemctl enable "${pkg}" || true
+                    systemctl start "${pkg}" || true
                     ;;
                 enabled)
-                    systemctl daemon-reload || true
-                    systemctl reload-or-try-restart "${systemd_unit}" || true
+                    systemctl reload-or-try-restart "${pkg}" || true
                     ;;
             esac
         fi
index c6ec18ca106cb1bba11e761363715fb1ce40c0d2..26baa62aa1b74266fd853062e5cb25bb4ec6a93d 100755 (executable)
@@ -2,26 +2,29 @@
 
 set -e
 
-# NOTE: This package name detection will only work on Debian.
-# If this prerm script ever starts doing work on Red Hat,
-# we'll need to adapt this code accordingly.
-script="$(basename "${0}")"
-pkg="${script%.prerm}"
-systemd_unit="${pkg}.service"
+if [ "%{name}" != "%\{name\}" ]; then
+    # Red Hat ("%{...}" is interpolated at package build time)
+    pkg="%{name}"
+    pkgtype=rpm
+    prefix="${RPM_INSTALL_PREFIX}"
+else
+    # Debian
+    script="$(basename "${0}")"
+    pkg="${script%.prerm}"
+    pkgtype=deb
+    prefix=/usr
+fi
 
-case "${1}" in
-    remove)
+case "${pkgtype}-${1}" in
+    deb-remove | rpm-0)
         if [ -e /run/systemd/system ]; then
-            systemctl stop "${systemd_unit}" || true
-            systemctl disable "${systemd_unit}" || true
+            systemctl stop "${pkg}" || true
+            systemctl disable "${pkg}" || true
         fi
-
-        # Unit files from Python packages get installed by postinst so
-        # we have to remove them explicitly here.
-        py_unit="/usr/share/doc/${pkg}/${pkg}.service"
-        if [ -e "${py_unit}" ]
-        then
-            rm "/lib/systemd/system/${pkg}.service" || true
+        if [ -e "${prefix}/share/doc/${pkg}/${pkg}.service" ]; then
+            # Unit files from Python packages get installed by
+            # postinst so we have to remove them explicitly here.
+            rm "/lib/systemd/system/${pkg}/${pkg}.service" || true
         fi
         ;;
 esac
index 5671a54ad58b2ae21e8443a683d2677224df6deb..6b7ec90e6394e4a2105ae4453d2648f56bfb7cc3 100644 (file)
@@ -3,39 +3,26 @@ h2. Configure the Docker cleaner
 The arvados-docker-cleaner program removes least recently used Docker images as needed to keep disk usage below a configured limit.
 
 {% include 'notebox_begin' %}
-This also removes all containers as soon as they exit, as if they were run with @docker run --rm@. If you need to debug or inspect containers after they stop, temporarily stop arvados-docker-cleaner or run it with @--remove-stopped-containers never@.
+This also removes all containers as soon as they exit, as if they were run with @docker run --rm@. If you need to debug or inspect containers after they stop, temporarily stop arvados-docker-cleaner or configure it with @"RemoveStoppedContainers":"never"@.
 {% include 'notebox_end' %}
 
-Create a file @/etc/systemd/system/arvados-docker-cleaner.service@ in an editor.  Include the text below as its contents.  Make sure to edit the @ExecStart@ line appropriately for your compute node.
+Create a file @/etc/arvados/docker-cleaner/docker-cleaner.json@ in an editor, with the following contents.
 
 <notextile>
-<pre><code>[Service]
-# Most deployments will want a quota that's at least 10G.  From there,
-# a larger quota can help reduce compute overhead by preventing reloading
-# the same Docker image repeatedly, but will leave less space for other
-# files on the same storage (usually Docker volumes).  Make sure the quota
-# is less than the total space available for Docker images.
-# If your deployment uses a Python 3 Software Collection, uncomment the
-# ExecStart line below, and delete the following one:
-# ExecStart=scl enable python33 "python3 -m arvados_docker.cleaner --quota <span class="userinput">20G</span>"
-ExecStart=python3 -m arvados_docker.cleaner --quota <span class="userinput">20G</span>
-Restart=always
-RestartPreventExitStatus=2
-
-[Install]
-WantedBy=default.target
-
-[Unit]
-After=docker.service
+<pre><code>{
+    "Quota": "<span class="userinput">10G</span>",
+    "RemoveStoppedContainers": "always"
+}
 </code></pre>
 </notextile>
 
-Then enable and start the service:
+*Choosing a quota:* Most deployments will want a quota that's at least 10G.  From there, a larger quota can help reduce compute overhead by preventing reloading the same Docker image repeatedly, but will leave less space for other files on the same storage (usually Docker volumes).  Make sure the quota is less than the total space available for Docker images.
+
+Restart the service after updating the configuration file.
 
 <notextile>
-<pre><code>~$ <span class="userinput">sudo systemctl enable arvados-docker-cleaner.service</span>
-~$ <span class="userinput">sudo systemctl start arvados-docker-cleaner.service</span>
+<pre><code>~$ <span class="userinput">sudo systemctl restart arvados-docker-cleaner</span>
 </code></pre>
 </notextile>
 
-If you are using a different daemon supervisor, or if you want to test the daemon in a terminal window, use the command on the @ExecStart@ line above.
+*If you are using a different daemon supervisor,* or if you want to test the daemon in a terminal window, run @arvados-docker-cleaner@. Run @arvados-docker-cleaner --help@ for more configuration options.
index 28653ae24ef153658484d5628cfeecdaea63f0dd..031058242a21179f15a0ef36ce9456f657811338 100644 (file)
@@ -6,9 +6,14 @@ AssertPathExists=/etc/arvados/docker-cleaner/docker-cleaner.json
 
 [Service]
 Type=simple
-ExecStart=/usr/bin/env arvados-docker-cleaner
 Restart=always
 RestartSec=10s
+RestartPreventExitStatus=2
+#
+# This unwieldy ExecStart command detects at runtime whether
+# arvados-docker-cleaner is installed with the Python 3.3 Software
+# Collection, and if so, invokes it with the "scl" wrapper.
+ExecStart=/bin/sh -c 'if [ -e /opt/rh/python33/root/bin/arvados-docker-cleaner ]; then exec scl enable python33 arvados-docker-cleaner; else exec arvados-docker-cleaner; fi'
 
 [Install]
 WantedBy=multi-user.target
index 5ac81004ace90c6a2b374d36b2af30d7eb5a2139..b03b9e50ffaa47921341305673975ea24b1bcac0 100755 (executable)
@@ -17,6 +17,8 @@ import time
 import docker
 import json
 
+DEFAULT_CONFIG_FILE = '/etc/arvados/docker-cleaner/docker-cleaner.json'
+
 SUFFIX_SIZES = {suffix: 1024 ** exp for exp, suffix in enumerate('kmgt', 1)}
 
 logger = logging.getLogger('arvados_docker.cleaner')
@@ -262,7 +264,14 @@ def load_config(arguments):
             c = json.load(f)
             config.update(c)
     except (FileNotFoundError, IOError, ValueError) as error:
-        sys.exit('error reading config file {}: {}'.format(args.config, error))
+        if (isinstance(error, FileNotFoundError) and
+            args.config == DEFAULT_CONFIG_FILE):
+            logger.warning("DEPRECATED: default config file %s not found; "
+                           "relying on command line configuration",
+                           repr(DEFAULT_CONFIG_FILE))
+        else:
+            sys.exit('error reading config file {}: {}'.format(
+                args.config, error))
 
     configargs = vars(args).copy()
     configargs.pop('config')
@@ -294,7 +303,7 @@ def parse_arguments(arguments):
         formatter_class=Formatter,
     )
     parser.add_argument(
-        '--config', action='store', type=str, default='/etc/arvados/docker-cleaner/docker-cleaner.json',
+        '--config', action='store', type=str, default=DEFAULT_CONFIG_FILE,
         help="configuration file")
 
     deprecated = " (DEPRECATED -- use config file instead)"
@@ -314,12 +323,15 @@ def parse_arguments(arguments):
     return parser.parse_args(arguments)
 
 
-def setup_logging(config):
+def setup_logging():
     log_handler = logging.StreamHandler()
     log_handler.setFormatter(logging.Formatter(
         '%(asctime)s %(name)s[%(process)d] %(levelname)s: %(message)s',
         '%Y-%m-%d %H:%M:%S'))
     logger.addHandler(log_handler)
+
+
+def configure_logging(config):
     logger.setLevel(logging.ERROR - (10 * config['Verbose']))
 
 
@@ -342,8 +354,9 @@ def run(config, docker_client):
 
 
 def main(arguments=sys.argv[1:]):
+    setup_logging()
     config = load_config(arguments)
-    setup_logging(config)
+    configure_logging(config)
     try:
         run(config, docker.Client(version='1.14'))
     except KeyboardInterrupt:
index 535650e3fa3f35bcbef48240727c7746e68eb7f8..7b7a471580788ca9ab857d1e5a9ba648af7c0077 100644 (file)
@@ -29,6 +29,7 @@ setup(name="arvados-docker-cleaner",
       ],
       install_requires=[
           'docker-py==1.7.2',
+          'setuptools',
       ],
       tests_require=[
           'pbr<1.7.0',
index 9fbd3e3014ecd0038d789d64ce3660d872268f6c..37c1d7600e34f9699b7788c0a76663173978d46f 100644 (file)
@@ -3,6 +3,7 @@
 import collections
 import itertools
 import json
+import os
 import random
 import tempfile
 import time
@@ -437,6 +438,11 @@ class ConfigTestCase(unittest.TestCase):
         self.assertEqual('never', config['RemoveStoppedContainers'])
         self.assertEqual(1, config['Verbose'])
 
+    def test_args_no_config(self):
+        self.assertEqual(False, os.path.exists(cleaner.DEFAULT_CONFIG_FILE))
+        config = cleaner.load_config(['--quota', '1G'])
+        self.assertEqual(1 << 30, config['Quota'])
+
 
 class ContainerRemovalTestCase(unittest.TestCase):
     LIFECYCLE = ['create', 'attach', 'start', 'resize', 'die', 'destroy']