Exception sanitization and unit tests
diff --git a/containers-api/src/main/java/org/apache/aries/containers/Service.java b/containers-api/src/main/java/org/apache/aries/containers/Service.java
index 6fe0a90..a185c17 100644
--- a/containers-api/src/main/java/org/apache/aries/containers/Service.java
+++ b/containers-api/src/main/java/org/apache/aries/containers/Service.java
@@ -32,7 +32,7 @@
     /**
      * Obtain the current instance count.
      *
-     * @return The instance count.
+     * @return The instance count. If the instance count cannot be obtained -1 is returned;
      */
     int getActualInstanceCount();
 
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerController.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerController.java
index 4904c20..dc8f297 100644
--- a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerController.java
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerController.java
@@ -19,6 +19,7 @@
 package org.apache.aries.containers.docker.local.impl;
 
 import java.io.BufferedReader;
+import java.io.IOException;
 import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.List;
@@ -47,7 +48,7 @@
         return new DockerContainerInfo(id, LocalDockerServiceManager.getContainerHost());
     }
 
-    public List<String> ps(String labelFilter) {
+    public List<String> ps(String labelFilter) throws IOException {
         String res = runCommand("docker", "ps", "-q", "--no-trunc","-f", "label=" + labelFilter);
 
         String[] sa = res.trim().split("\\s+");
@@ -60,7 +61,7 @@
         return sl;
     }
 
-    public String inspect(List<String> ids) {
+    public String inspect(List<String> ids) throws IOException {
         if (ids.size() == 0)
             return "[]";
 
@@ -71,7 +72,7 @@
         return runCommand(cmd.toArray(new String [] {}));
     }
 
-    String runCommandExpectSingleID(String ... command) throws Exception {
+    String runCommandExpectSingleID(String ... command) throws IOException {
         String res = runCommand(command);
         if (res != null) {
             res = res.trim();
@@ -83,7 +84,7 @@
                 }
             }
             if (lastLine.indexOf(' ') != -1 ) {
-                 throw new Exception("Unable to execute docker command: " + res);
+                 throw new IOException("Unable to execute docker command: " + res);
             }
             res = lastLine;
         }
@@ -91,7 +92,7 @@
         return res;
     }
 
-    String runCommand(String... command) {
+    String runCommand(String... command) throws IOException {
         return ProcessRunner.waitFor(ProcessRunner.run(command));
     }
 }
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerServiceManager.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerServiceManager.java
index feced50..c51484f 100644
--- a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerServiceManager.java
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerServiceManager.java
@@ -55,10 +55,18 @@
             .anyMatch(path -> Files.exists(path.resolve("docker-machine")));
 
     private static final boolean USE_DOCKER_MACHINE = (DOCKER_MACHINE_VM_NAME != null) && CHECK_DOCKER_MACHINE;
-    static final String CONTAINER_HOST = USE_DOCKER_MACHINE
-            ? ProcessRunner.waitFor(ProcessRunner.run("docker-machine", "ip", DOCKER_MACHINE_VM_NAME))
-            : "localhost";
-
+    static final String CONTAINER_HOST;
+    static {
+        if (USE_DOCKER_MACHINE) {
+            try {
+                CONTAINER_HOST = ProcessRunner.waitFor(ProcessRunner.run("docker-machine", "ip", DOCKER_MACHINE_VM_NAME));
+            } catch (IOException e) {
+                throw new RuntimeException("Problem invoking docker-machine", e);
+            }
+        } else {
+            CONTAINER_HOST = "localhost";
+        }
+    }
 
     private final LocalDockerController docker;
     final ConcurrentMap<String, Service> services =
@@ -72,7 +80,7 @@
         this.docker = docker;
     }
 
-    List<String> getDockerIDs(ServiceConfig config) {
+    List<String> getDockerIDs(ServiceConfig config) throws IOException {
         return docker.ps(SERVICE_NAME_LABEL + "=" + config.getServiceName());
     }
 
@@ -155,7 +163,7 @@
     }
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
-    List<ContainerImpl> discoverContainers(ServiceConfig config) {
+    List<ContainerImpl> discoverContainers(ServiceConfig config) throws IOException {
         List<ContainerImpl> res = new ArrayList<>();
         List<String> ids = getDockerIDs(config);
         if (ids.size() == 0)
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ProcessRunner.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ProcessRunner.java
index 18e7ac0..a634d7e 100644
--- a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ProcessRunner.java
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ProcessRunner.java
@@ -20,7 +20,6 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Map;
 
@@ -57,35 +56,30 @@
 
         LOG.info("Executing shell command: {} with environment {}", args, envVars);
 
-        try {
-            ProcessBuilder builder = new ProcessBuilder(args);
-            if ( dir != null ) {
-                builder.directory(dir);
-            }
-            builder.redirectErrorStream(true);
-            Map<String, String> environ = builder.environment();
-            environ.putAll(envVars);
-
-            Process process = builder.start();
-
-            return process;
-        } catch (IOException e) {
-            LOG.error("Problem executing command: " + Arrays.toString(args), e);
-            throw e;
+        ProcessBuilder builder = new ProcessBuilder(args);
+        if ( dir != null ) {
+            builder.directory(dir);
         }
+        builder.redirectErrorStream(true);
+        Map<String, String> environ = builder.environment();
+        environ.putAll(envVars);
+
+        Process process = builder.start();
+
+        return process;
     }
 
-    public static String waitFor(final Process process) {
+    public static String waitFor(final Process process) throws IOException {
         if (process == null)
             return null;
 
         try {
             process.waitFor();
-            String res = new String(Streams.suck(process.getInputStream())).trim();
-            LOG.debug("Result: {}", res);
-            return res;
-        } catch (IOException | InterruptedException e) {
-            throw new RuntimeException(e);
+        } catch (InterruptedException e) {
+            // Ignore
         }
+        String res = new String(Streams.suck(process.getInputStream())).trim();
+        LOG.debug("Result: {}", res);
+        return res;
     }
 }
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ServiceImpl.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ServiceImpl.java
index bf0a229..0bea674 100644
--- a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ServiceImpl.java
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ServiceImpl.java
@@ -18,6 +18,7 @@
  */
 package org.apache.aries.containers.docker.local.impl;
 
+import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -46,7 +47,13 @@
 
     @Override
     public int getActualInstanceCount() {
-        return factory.getDockerIDs(config).size();
+        try {
+            return factory.getDockerIDs(config).size();
+        } catch (IOException e) {
+            LocalDockerServiceManager.LOG.warn(
+                    "Cannot obtain docker instance count for service {}", config.getServiceName(), e);
+            return -1;
+        }
     }
 
     @Override
@@ -88,9 +95,13 @@
     @Override
     public void refresh() {
         containers.clear();
-        for (ContainerImpl c : factory.discoverContainers(config)) {
-            c.setService(this);
-            containers.add(c);
+        try {
+            for (ContainerImpl c : factory.discoverContainers(config)) {
+                c.setService(this);
+                containers.add(c);
+            }
+        } catch (IOException e) {
+            LocalDockerServiceManager.LOG.error("Problem refreshing service {}", config.getServiceName(), e);
         }
     }
 
diff --git a/containers-docker-local/src/test/java/org/apache/aries/containers/docker/local/impl/LocalDockerControllerTest.java b/containers-docker-local/src/test/java/org/apache/aries/containers/docker/local/impl/LocalDockerControllerTest.java
index 4581a35..4edd1b1 100644
--- a/containers-docker-local/src/test/java/org/apache/aries/containers/docker/local/impl/LocalDockerControllerTest.java
+++ b/containers-docker-local/src/test/java/org/apache/aries/containers/docker/local/impl/LocalDockerControllerTest.java
@@ -18,6 +18,10 @@
  */
 package org.apache.aries.containers.docker.local.impl;
 
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -40,7 +44,7 @@
     public void testKill() throws Exception {
         LocalDockerController ldc = new LocalDockerController() {
             @Override
-            String runCommandExpectSingleID(String... command) throws Exception {
+            String runCommandExpectSingleID(String... command) throws IOException {
                 assertArrayEquals(new String [] {"docker", "kill", "-s", "KILL", "123abc"}, command);
                 return "ok";
             }
@@ -52,7 +56,7 @@
     public void testKillSignal() throws Exception {
         LocalDockerController ldc = new LocalDockerController() {
             @Override
-            String runCommandExpectSingleID(String... command) throws Exception {
+            String runCommandExpectSingleID(String... command) throws IOException {
                 assertArrayEquals(new String [] {"docker", "kill", "-s", "TERM", "123abc"}, command);
                 return "ok";
             }
@@ -64,11 +68,54 @@
     public void testRemove() throws Exception {
         LocalDockerController ldc = new LocalDockerController() {
             @Override
-            String runCommandExpectSingleID(String... command) throws Exception {
+            String runCommandExpectSingleID(String... command) throws IOException {
                 assertArrayEquals(new String [] {"docker", "rm", "-f", "123abc"}, command);
                 return "ok";
             }
         };
         assertEquals("ok", ldc.remove("123abc"));
     }
+
+    @Test
+    public void testRun() throws Exception {
+        LocalDockerController ldc = new LocalDockerController() {
+            @Override
+            String runCommand(String... command) throws IOException {
+                assertArrayEquals(new String [] {
+                        "docker", "run", "-it", "-p", "8080:8080", "myimg"}, command);
+                return "ok";
+            }
+        };
+        DockerContainerInfo info = ldc.run(Arrays.asList("-it", "-p", "8080:8080", "myimg"));
+        assertEquals("ok", info.getID());
+        assertEquals(LocalDockerServiceManager.getContainerHost(), info.getIP());
+    }
+
+    @Test
+    public void testPS() throws Exception {
+        LocalDockerController ldc = new LocalDockerController() {
+
+            @Override
+            String runCommand(String... command) {
+                assertArrayEquals(new String [] {
+                        "docker", "ps", "-q", "--no-trunc","-f", "label=mylabel"}, command);
+                return "\n a\nb\nc \n\n";
+            }
+        };
+        assertEquals(Arrays.asList("a", "b", "c"), ldc.ps("mylabel"));
+    }
+
+    @Test
+    public void testPS2() throws Exception {
+        LocalDockerController ldc = new LocalDockerController() {
+
+            @Override
+            String runCommand(String... command) {
+                assertArrayEquals(new String [] {
+                        "docker", "ps", "-q", "--no-trunc","-f", "label=mylabel"}, command);
+                return "\n";
+            }
+        };
+        assertEquals(Collections.emptyList(), ldc.ps("mylabel"));
+    }
 }