Repository: docker-java/docker-java
Pull request #262 (2015-07-07 13:06:44)

Accept filters in list containers
PR on top of #260, to test with container labels


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 506a1825..b76a052d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,7 @@
 Change Log
 ===
-
 Latest SNAPSHOT
 ---
-
-v.1.4.0
----
 * [#248](https://github.com/docker-java/docker-java/pull/233) Removed deprecated start options
 * [#247](https://github.com/docker-java/docker-java/pull/247) Add Domainname attribute on create command
 * [#245](https://github.com/docker-java/docker-java/pull/245) Added ReadonlyRootfs option
diff --git a/README.md b/README.md
index 52d79192..a4c18a0c 100644
--- a/README.md
+++ b/README.md
@@ -64,7 +64,7 @@ Run build without integration tests:
     <dependency>
           <groupId>com.github.docker-java</groupId>
           <artifactId>docker-java</artifactId>
-          <version>1.4.0</version>
+          <version>1.3.0</version>
     </dependency>
 
 ### Latest SNAPSHOT version
@@ -74,7 +74,7 @@ You can find the latest SNAPSHOT version including javadoc and source files on [
     <dependency>
           <groupId>com.github.docker-java</groupId>
           <artifactId>docker-java</artifactId>
-          <version>1.4.1-SNAPSHOT</version>
+          <version>1.3.1-SNAPSHOT</version>
     </dependency>
 
 ## Documentation
diff --git a/pom.xml b/pom.xml
index 7618263a..30ece10a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,4 +1,5 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 
 	<parent>
@@ -10,7 +11,7 @@
 	<groupId>com.github.docker-java</groupId>
 	<artifactId>docker-java</artifactId>
 	<packaging>jar</packaging>
-	<version>1.4.1-SNAPSHOT</version>
+	<version>1.4.0-SNAPSHOT</version>
 
 	<name>docker-java</name>
 	<url>https://github.com/docker-java/docker-java</url>
diff --git a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java
index 3b558b6d..3e31321e 100644
--- a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java
+++ b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java
@@ -1,5 +1,7 @@
 package com.github.dockerjava.api.command;
 
+import java.util.Map;
+
 import com.github.dockerjava.api.ConflictException;
 import com.github.dockerjava.api.NotFoundException;
 import com.github.dockerjava.api.model.Bind;
@@ -173,6 +175,8 @@ public interface CreateContainerCmd extends DockerCmd<CreateContainerResponse> {
 
     public CreateContainerCmd withImage(String image);
 
+    public CreateContainerCmd withLabels(Map<String, String> labels);
+
     /**
      * Add link to another container.
      */
@@ -238,4 +242,9 @@ public interface CreateContainerCmd extends DockerCmd<CreateContainerResponse> {
 
     public CreateContainerCmd withMacAddress(String macAddress);
 
+    /**
+     * @return
+     */
+    Map<String, String> getLabels();
+
 }
diff --git a/src/main/java/com/github/dockerjava/api/command/ListContainersCmd.java b/src/main/java/com/github/dockerjava/api/command/ListContainersCmd.java
index 6d641f2e..c2a116be 100644
--- a/src/main/java/com/github/dockerjava/api/command/ListContainersCmd.java
+++ b/src/main/java/com/github/dockerjava/api/command/ListContainersCmd.java
@@ -3,6 +3,7 @@ package com.github.dockerjava.api.command;
 import java.util.List;
 
 import com.github.dockerjava.api.model.Container;
+import com.github.dockerjava.api.model.Filters;
 
 /**
  * List containers
@@ -31,6 +32,8 @@ public interface ListContainersCmd extends DockerCmd<List<Container>> {
 
     public String getBeforeId();
 
+    public Filters getFilters();
+
     public ListContainersCmd withShowAll(boolean showAll);
 
     public ListContainersCmd withShowSize(boolean showSize);
@@ -41,6 +44,8 @@ public interface ListContainersCmd extends DockerCmd<List<Container>> {
 
     public ListContainersCmd withBefore(String before);
 
+    public ListContainersCmd withFilters(Filters filters);
+
     public static interface Exec extends DockerCmdExec<ListContainersCmd, List<Container>> {
     }
 
diff --git a/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java b/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java
index 8ea673a3..0478e5ef 100644
--- a/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java
+++ b/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java
@@ -51,6 +51,9 @@ public class ContainerConfig {
     @JsonProperty("Image")
     private String image;
 
+    @JsonProperty("Labels")
+    private Map<String, String> labels;
+
     @JsonProperty("MacAddress")
     private String macAddress;
 
@@ -184,6 +187,10 @@ public class ContainerConfig {
         return onBuild;
     }
 
+    public Map<String, String> getLabels() {
+        return labels;
+    }
+
     @Override
     public String toString() {
         return ToStringBuilder.reflectionToString(this);
diff --git a/src/main/java/com/github/dockerjava/api/model/Filters.java b/src/main/java/com/github/dockerjava/api/model/Filters.java
new file mode 100644
index 00000000..a226c60c
--- /dev/null
+++ b/src/main/java/com/github/dockerjava/api/model/Filters.java
@@ -0,0 +1,90 @@
+package com.github.dockerjava.api.model;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.MediaType;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
+
+/**
+ * Representation of Docker filters.
+ *
+ * @author Carlos Sanchez <carlos@apache.org>
+ *
+ */
+public class Filters {
+
+    private static ObjectMapper OBJECT_MAPPER = new JacksonJaxbJsonProvider().locateMapper(Map.class,
+            MediaType.APPLICATION_JSON_TYPE);
+
+    private Map<String, List<String>> filters = new HashMap<String, List<String>>();
+
+    public Filters() {
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param image
+     *            image to filter
+     * @param container
+     *            container to filter
+     */
+    public Filters(String image, String container) {
+        withImage(image);
+        withContainer(container);
+    }
+
+    public Filters withFilter(String key, String... value) {
+        filters.put(key, Arrays.asList(value));
+        return this;
+    }
+
+    public List<String> getFilter(String key) {
+        return filters.get(key);
+    }
+
+    public Filters withImage(String... image) {
+        filters.put("image", Arrays.asList(image));
+        return this;
+    }
+
+    public List<String> getImage() {
+        return getFilter("image");
+    }
+
+    public Filters withContainer(String... container) {
+        filters.put("container", Arrays.asList(container));
+        return this;
+    }
+
+    public List<String> getContainer() {
+        return getFilter("container");
+    }
+
+    /**
+     * Filter by labels
+     * 
+     * @param labels
+     *            string array in the form ["key"] or ["key=value"] or a mix of both
+     * @return
+     */
+    public Filters withLabel(String... labels) {
+        filters.put("label", Arrays.asList(labels));
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        try {
+            return OBJECT_MAPPER.writeValueAsString(filters);
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java
index cc082b61..3caf3c1b 100644
--- a/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java
+++ b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java
@@ -2,6 +2,8 @@ package com.github.dockerjava.core.command;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.Map;
+
 import org.apache.commons.lang.builder.ToStringBuilder;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -107,6 +109,9 @@ public class CreateContainerCmdImpl extends AbstrDockerCmd<CreateContainerCmd, C
     @JsonProperty("HostConfig")
     private HostConfig hostConfig = new HostConfig();
 
+    @JsonProperty("Labels")
+    private Map<String, String> labels;
+
     public CreateContainerCmdImpl(CreateContainerCmd.Exec exec, String image) {
         super(exec);
         checkNotNull(image, "image was not specified");
@@ -221,6 +226,12 @@ public class CreateContainerCmdImpl extends AbstrDockerCmd<CreateContainerCmd, C
         return hostConfig.getLinks();
     }
 
+    @Override
+    @JsonIgnore
+    public Map<String, String> getLabels() {
+        return labels;
+    }
+
     @Override
     @JsonIgnore
     public LxcConf[] getLxcConf() {
@@ -471,6 +482,13 @@ public class CreateContainerCmdImpl extends AbstrDockerCmd<CreateContainerCmd, C
         return this;
     }
 
+    @Override
+    public CreateContainerCmdImpl withLabels(Map<String, String> labels) {
+        checkNotNull(labels, "labels was not specified");
+        this.labels = labels;
+        return this;
+    }
+
     @Override
     public CreateContainerCmdImpl withLinks(Link... links) {
         checkNotNull(links, "links was not specified");
diff --git a/src/main/java/com/github/dockerjava/core/command/ListContainersCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/ListContainersCmdImpl.java
index 13bb02cf..b957a3bd 100644
--- a/src/main/java/com/github/dockerjava/core/command/ListContainersCmdImpl.java
+++ b/src/main/java/com/github/dockerjava/core/command/ListContainersCmdImpl.java
@@ -1,12 +1,12 @@
 package com.github.dockerjava.core.command;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.*;
 
 import java.util.List;
 
 import com.github.dockerjava.api.command.ListContainersCmd;
 import com.github.dockerjava.api.model.Container;
+import com.github.dockerjava.api.model.Filters;
 
 /**
  * List containers
@@ -32,6 +32,8 @@ public class ListContainersCmdImpl extends AbstrDockerCmd<ListContainersCmd, Lis
 
     private String sinceId, beforeId;
 
+    private Filters filters;
+
     public ListContainersCmdImpl(ListContainersCmd.Exec exec) {
         super(exec);
     }
@@ -61,6 +63,11 @@ public class ListContainersCmdImpl extends AbstrDockerCmd<ListContainersCmd, Lis
         return beforeId;
     }
 
+    @Override
+    public Filters getFilters() {
+        return filters;
+    }
+
     @Override
     public ListContainersCmd withShowAll(boolean showAll) {
         this.showAll = showAll;
@@ -94,11 +101,18 @@ public class ListContainersCmdImpl extends AbstrDockerCmd<ListContainersCmd, Lis
         return this;
     }
 
+    @Override
+    public ListContainersCmd withFilters(Filters filters) {
+        checkNotNull(filters, "filters was not specified");
+        this.filters = filters;
+        return this;
+    }
+
     @Override
     public String toString() {
         return new StringBuilder("ps ").append(showAll ? "--all=true" : "").append(showSize ? "--size=true" : "")
-                .append(sinceId != null ? "--since " + sinceId : "")
-                .append(beforeId != null ? "--before " + beforeId : "").append(limit != -1 ? "-n " + limit : "")
-                .toString();
+                .append(sinceId != null ? " --since " + sinceId : "")
+                .append(beforeId != null ? " --before " + beforeId : "").append(limit != -1 ? "-n " + limit : "")
+                .append(filters != null ? " --filters " + filters : "").toString();
     }
 }
diff --git a/src/main/java/com/github/dockerjava/jaxrs/ListContainersCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/ListContainersCmdExec.java
index 5bb905b5..5feb5bda 100644
--- a/src/main/java/com/github/dockerjava/jaxrs/ListContainersCmdExec.java
+++ b/src/main/java/com/github/dockerjava/jaxrs/ListContainersCmdExec.java
@@ -1,5 +1,7 @@
 package com.github.dockerjava.jaxrs;
 
+import static com.google.common.net.UrlEscapers.*;
+
 import java.util.List;
 
 import javax.ws.rs.client.WebTarget;
@@ -32,6 +34,11 @@ public class ListContainersCmdExec extends AbstrDockerCmdExec<ListContainersCmd,
             webResource = webResource.queryParam("limit", String.valueOf(command.getLimit()));
         }
 
+        if (command.getFilters() != null) {
+            webResource = webResource.queryParam("filters",
+                    urlPathSegmentEscaper().escape(command.getFilters().toString()));
+        }
+
         LOGGER.trace("GET: {}", webResource);
         List<Container> containers = webResource.request().accept(MediaType.APPLICATION_JSON)
                 .get(new GenericType<List<Container>>() {


Test patch:
diff --git a/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java
index a33cfddf..efeed530 100644
--- a/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java
+++ b/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java
@@ -13,6 +13,8 @@ import org.testng.annotations.*;
 import java.lang.reflect.Method;
 import java.security.SecureRandom;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.UUID;
 
 import static com.github.dockerjava.api.model.Capability.MKNOD;
@@ -490,4 +492,24 @@ public class CreateContainerCmdImplTest extends AbstractDockerClientTest {
 
     }
 
+    @Test
+    public void createContainerWithLabels() throws DockerException {
+
+        Map<String, String> labels = new HashMap<String, String>();
+        labels.put("com.github.dockerjava.null", null);
+        labels.put("com.github.dockerjava.boolean", "true");
+
+        CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999")
+                .withLabels(labels).exec();
+
+        LOG.info("Created container {}", container.toString());
+
+        assertThat(container.getId(), not(isEmptyString()));
+
+        InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec();
+
+        // null becomes empty string
+        labels.put("com.github.dockerjava.null", "");
+        assertThat(inspectContainerResponse.getConfig().getLabels(), is(equalTo(labels)));
+    }
 }
diff --git a/src/test/java/com/github/dockerjava/core/command/ListContainersCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ListContainersCmdImplTest.java
index b074409b..da049488 100644
--- a/src/test/java/com/github/dockerjava/core/command/ListContainersCmdImplTest.java
+++ b/src/test/java/com/github/dockerjava/core/command/ListContainersCmdImplTest.java
@@ -26,7 +26,9 @@ import com.github.dockerjava.api.DockerException;
 import com.github.dockerjava.api.command.CreateContainerResponse;
 import com.github.dockerjava.api.command.InspectContainerResponse;
 import com.github.dockerjava.api.model.Container;
+import com.github.dockerjava.api.model.Filters;
 import com.github.dockerjava.client.AbstractDockerClientTest;
+import com.google.common.collect.ImmutableMap;
 
 @Test(groups = "integration")
 public class ListContainersCmdImplTest extends AbstractDockerClientTest {
@@ -96,7 +98,24 @@ public class ListContainersCmdImplTest extends AbstractDockerClientTest {
 
         Container container2 = filteredContainers.get(0);
         assertThat(container2.getCommand(), not(isEmptyString()));
-        assertThat(container2.getImage(), startsWith(testImage + ":"));
+        assertThat(container2.getImage(), startsWith(testImage));
+
+        // list with filter by label
+        dockerClient.createContainerCmd(testImage).withCmd("echo").withLabels(ImmutableMap.of("test", "docker-java"))
+                .exec();
+        filteredContainers = dockerClient.listContainersCmd().withShowAll(true)
+                .withFilters(new Filters().withLabel("test=docker-java")).exec();
+        assertThat(filteredContainers.size(), is(equalTo(1)));
+        Container container3 = filteredContainers.get(0);
+        assertThat(container3.getCommand(), not(isEmptyString()));
+        assertThat(container3.getImage(), startsWith(testImage));
+
+        filteredContainers = dockerClient.listContainersCmd().withShowAll(true)
+                .withFilters(new Filters().withLabel("test")).exec();
+        assertThat(filteredContainers.size(), is(equalTo(1)));
+        container3 = filteredContainers.get(0);
+        assertThat(container3.getCommand(), not(isEmptyString()));
+        assertThat(container3.getImage(), startsWith(testImage));
     }
 
 }