diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..45958bd2 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms +custom: https://paypal.me/amihaiemil +github: amihaiemil diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 00000000..8a060bb0 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,23 @@ +name: Java CI + +on: [push] + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-18.04] + java: [8, 8.0.192, 11, 11.0.2, 13, 13.0.4, 15, 16-ea] + fail-fast: false + max-parallel: 2 + name: Test JDK ${{ matrix.java }}, ${{ matrix.os }} + + steps: + - uses: actions/checkout@v1 + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + - name: Test with Maven + run: mvn test -B --file pom.xml diff --git a/.travis.yml b/.travis.yml index feeb4e69..8fdced94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,11 @@ sudo: required -services: - - docker +before_install: + - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" + - sudo apt-get update + - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce +#services: +# - docker language: java jdk: - openjdk8 @@ -12,5 +17,4 @@ script: - docker pull hello-world #for some IT cases - docker pull ubuntu # for container IT cases - docker run -d --privileged -p 12375:2375 docker:dind - - mvn clean install -Pcheckstyle,itcases - #jacoco:report coveralls:report + - mvn clean install -Pcheckstyle,itcases -PtestCoverage jacoco:report coveralls:report diff --git a/README.md b/README.md index 81eccc38..58eaa448 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,36 @@ The library comes as a maven dependency: com.amihaiemil.web docker-java-api - 0.0.11 + 0.0.13 ``` -**In order for it to work, you need to have an implementation of [JSON-P (JSR 374)](https://javaee.github.io/jsonp/index.html) in your classpath (it doesn't come transitively since most people are using Java EE APIs so, chances are it is already provided!).** +**In order for it to work, you need to have an implementation of [JSON-P (JSR 374)](https://javaee.github.io/jsonp/index.html) in your classpath (it doesn't come transitively since most people are using Java EE APIs so, chances are it is already provided!). If you have no idea what this means, go [here](https://github.com/amihaiemil/docker-java-api/wiki/What-It-Is#implementation).** -If you are not using Maven, you can also download the fat jar. +If you are not using Maven, you can also download the fat jar. + +### Usage Example And Wiki + +Here is all you need to do in order to pull an Image and run a Container into the local Docker engine: + +```java +final Container started = new UnixDocker(new File("/var/run/docker.sock")) + .images() + .pull("hello-world", "latest") + .run(); +``` +or, the same code snippet, less fluent: +```java +final Docker docker = new UnixDocker(new File("/var/run/docker.sock")); +final Images images = docker.images(); +for(final Image image : images){ +//iterate over the existing images +} +final Image helloWorld = images.pull("hello-world", "latest"); +final Container started = helloWorld.run(); +``` + +More info in the [Wiki](https://github.com/amihaiemil/docker-java-api/wiki). ### Contributing diff --git a/checkstyle.xml b/checkstyle.xml index 842eabab..49a04c3f 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -72,11 +72,11 @@ - + + 0.0.14-SNAPSHOT Docker Java API Elegant OOP wrapper over Docker's API https://www.github.com/amihaiemil/docker-java-api @@ -68,7 +68,7 @@ junit junit - 4.12 + 4.13.1 test @@ -107,27 +107,6 @@ 1.8 - @@ -167,6 +146,39 @@ + + testCoverage + + + + org.jacoco + jacoco-maven-plugin + 0.8.5 + + + prepare-agent + + prepare-agent + + + + + + **/YamlStream.* + + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.2.0 + + false + + + + + itcases diff --git a/src/main/java/com/amihaiemil/docker/Container.java b/src/main/java/com/amihaiemil/docker/Container.java index 8693634b..62dd1b75 100644 --- a/src/main/java/com/amihaiemil/docker/Container.java +++ b/src/main/java/com/amihaiemil/docker/Container.java @@ -151,5 +151,26 @@ void remove(final boolean volumes, final boolean force, final boolean link) * the expected one (204 NO CONTENT). */ void unpause() throws IOException; - + + + /** + * Waits on this container. + * @param state The state to wait for. One of "not-running" + * (the default if null), "next-exit", or "removed" + * @see Wait Container. + * @throws IOException If something goes wrong. + * the expected one (200). + * @return The exit code of the container + */ + int waitOn(String state) throws IOException; + + /** + * Create a Exec. + * @param config Exec configuration. + * @see Create Exec + * @return Exec created. + * @throws IOException If something goes wrong. + */ + Exec exec(final JsonObject config) throws IOException; + } diff --git a/src/main/java/com/amihaiemil/docker/Containers.java b/src/main/java/com/amihaiemil/docker/Containers.java index 98eebf92..bd510605 100644 --- a/src/main/java/com/amihaiemil/docker/Containers.java +++ b/src/main/java/com/amihaiemil/docker/Containers.java @@ -74,7 +74,7 @@ Container create( * @throws IOException If something goes wrong. */ Container create(final JsonObject container) throws IOException; - + /** * Return all Containers, not only the running ones. * @return Iterator over all the containers. @@ -95,10 +95,17 @@ Container create( * @see Docker API Docs */ Containers filter(Map> filters); - + /** * Return the Docker engine where these Containers came from. * @return Docker. */ Docker docker(); + + /** + * Get this container.

+ * @param containerId Id of the Container + * @return This container object. + */ + Container get(final String containerId); } diff --git a/src/main/java/com/amihaiemil/docker/Docker.java b/src/main/java/com/amihaiemil/docker/Docker.java index d35e7927..2c5afed3 100644 --- a/src/main/java/com/amihaiemil/docker/Docker.java +++ b/src/main/java/com/amihaiemil/docker/Docker.java @@ -26,8 +26,6 @@ package com.amihaiemil.docker; import java.io.IOException; -import java.io.Reader; - import org.apache.http.client.HttpClient; /** @@ -46,14 +44,10 @@ public interface Docker { boolean ping() throws IOException; /** - * Follow the events on the server in real time. - * @return The events {@link Reader}. - * @throws IOException If an I/O error occurs. - * @throws UnexpectedResponseException If the API responds with an - * unexpected status. + * Entry point for the Events API. + * @return Events. */ - Reader events() - throws IOException, UnexpectedResponseException; + Events events(); /** * Entry point for the Containers API. @@ -81,9 +75,9 @@ Reader events() /** * Entry point for the Exec API. - * @return Exec. + * @return Execs. */ - Exec exec(); + Execs execs(); /** * Entry point for the Swarm API. @@ -104,12 +98,19 @@ Reader events() Plugins plugins(); /** - * Entry point for Version API. + * Version of this Docker engine. * @return Version. * @throws IOException If an I/O error occurs. */ Version version() throws IOException; + /** + * Detailed information about this Docker engine, in JSON. + * @return Info. + * @throws IOException If an I/O problem occurs. + */ + Info info() throws IOException; + /** * The underlying, immutable, Apache HttpClient.

* diff --git a/src/main/java/com/amihaiemil/docker/Events.java b/src/main/java/com/amihaiemil/docker/Events.java new file mode 100644 index 00000000..2d01ba17 --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/Events.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2018-2020, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import javax.json.JsonObject; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * Events API. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.13 + */ +public interface Events { + + /** + * Show events created since the specified timestamp, + * then stream new events. + * @param timestamp Given timestamp. + * @return Filtered Events. + */ + Events since(final LocalDateTime timestamp); + + /** + * Show events created until the specified timestamp, + * then stop streaming. + * @param timestamp Given timestamp. + * @return Filtered Events. + */ + Events until(final LocalDateTime timestamp); + + /** + * Filter these Events. + * @param filter Supplier of filters. + * @return Filtered Events. + * @see Docker API Docs + */ + Events filter(final Supplier>> filter); + + /** + * Start monitoring these events. Pay attention:

+ * The Stream is infinite, which means you have to specify a + * limit before calling a terminal operation on it. Otherwise, + * your terminal operation will run until the Server closes the connection. + * Example: + *
+     *   final Docker docker = ...;
+     *   final List<JsonObject> firstTen = docker.events().monitor()
+     *       .limit(10).collect(Collectors.toList());
+     *   //It will wait for and return the first 10 real-time events
+     *   //from the server.
+     * 
+ * @throws IOException If there is any I/O problem. + * @throws UnexpectedResponseException If the response is not 200 OK. + * @return Stream of events. + */ + Stream monitor() + throws IOException, UnexpectedResponseException; + + /** + * Docker where these events came from. + * @return Docker. + */ + Docker docker(); +} diff --git a/src/main/java/com/amihaiemil/docker/Exec.java b/src/main/java/com/amihaiemil/docker/Exec.java index 20f006cf..f44da19f 100644 --- a/src/main/java/com/amihaiemil/docker/Exec.java +++ b/src/main/java/com/amihaiemil/docker/Exec.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018-2019, Mihai Emil Andronache + * Copyright (c) 2018-2020, Mihai Emil Andronache * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,11 +25,25 @@ */ package com.amihaiemil.docker; +import javax.json.JsonObject; +import java.io.IOException; + /** - * Exec API. + * Exec containing the commands to be run within a Container. * @author Mihai Andronache (amihaiemil@gmail.com) * @version $Id$ * @since 0.0.1 */ public interface Exec { + + /** + * Return JSON information about this Ezec. + * @return JsonObject information. + * @see Inspect Exec + * @throws IOException If something goes wrong. + * @throws UnexpectedResponseException If the status response is not + * the expected one (200 OK). + */ + JsonObject inspect() throws IOException, UnexpectedResponseException; + } diff --git a/src/main/java/com/amihaiemil/docker/Execs.java b/src/main/java/com/amihaiemil/docker/Execs.java new file mode 100644 index 00000000..08e9ad23 --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/Execs.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2018-2020, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +/** + * The Exec API. More about it here:
+ * https://docs.docker.com/engine/api/v1.40/#tag/Exec + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.12 + */ +public interface Execs { + + /** + * Get the Exec with the specified ID. + * @param execId The ID of the searched Exec. + * @return Exec. + */ + Exec get(final String execId); + + /** + * Return the Docker engine where these Execs came from. + * @return Docker. + */ + Docker docker(); + +} diff --git a/src/main/java/com/amihaiemil/docker/Info.java b/src/main/java/com/amihaiemil/docker/Info.java new file mode 100644 index 00000000..42535c42 --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/Info.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2018-2020, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import javax.json.JsonObject; + +/** + * General information about the Docker engine. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.13 + */ +public interface Info extends JsonObject { + + /** + * The Docker engine to which this Info refers to. + * @return Docker. + */ + Docker docker(); +} diff --git a/src/main/java/com/amihaiemil/docker/Inspection.java b/src/main/java/com/amihaiemil/docker/Inspection.java index c1b875d4..21bc661e 100644 --- a/src/main/java/com/amihaiemil/docker/Inspection.java +++ b/src/main/java/com/amihaiemil/docker/Inspection.java @@ -32,7 +32,7 @@ import org.apache.http.client.methods.HttpGet; /** - * An inpsection upon any of the Docker resources. + * An inspection upon any of the Docker resources. * @author George Aristy (george.aristy@gmail.com) * @version $Id$ * @since 0.0.1 diff --git a/src/main/java/com/amihaiemil/docker/ListedNetworks.java b/src/main/java/com/amihaiemil/docker/ListedNetworks.java index 5d5101e3..a2435ed3 100644 --- a/src/main/java/com/amihaiemil/docker/ListedNetworks.java +++ b/src/main/java/com/amihaiemil/docker/ListedNetworks.java @@ -97,4 +97,18 @@ public Iterator iterator() { ) ); } + + @Override + public Networks filter(final Map> fltrs) { + final Map> merged = new HashMap<>( + this.filters + ); + merged.putAll(fltrs); + return new ListedNetworks( + super.client(), + this.baseUri(), + this.docker(), + merged + ); + } } diff --git a/src/main/java/com/amihaiemil/docker/LocalDocker.java b/src/main/java/com/amihaiemil/docker/LocalDocker.java index 2783d134..b5443099 100644 --- a/src/main/java/com/amihaiemil/docker/LocalDocker.java +++ b/src/main/java/com/amihaiemil/docker/LocalDocker.java @@ -33,18 +33,24 @@ * Local Docker API. Use this when you want to communicate with the local * Docker engine. * + * @deprecated Please use UnixDocker instead. It does + * exactly the same thing, it's just a change for a more suitable + * name. This class will be removed in one of the future releases. + * *
- *     final Docker docker = new LocalDocker("unix:///var/run/dicker.sock");
+ *     final Docker docker = new LocalUnixDocker("unix:///var/run/dicker.sock");
  * 
* * This implementation manages an internal pool of 10 http connections. Users * who wish to alter this behaviour may provide their own {@link HttpClient} - * via {@link #LocalDocker(HttpClient, String)}. + * via the specific constructor. * * @author Mihai Andronache (amihaiemil@gmail.com) * @version $Id$ * @since 0.0.1 */ + +@Deprecated public final class LocalDocker extends RtDocker { /** diff --git a/src/main/java/com/amihaiemil/docker/Networks.java b/src/main/java/com/amihaiemil/docker/Networks.java index b2a9bbe7..0d807a05 100644 --- a/src/main/java/com/amihaiemil/docker/Networks.java +++ b/src/main/java/com/amihaiemil/docker/Networks.java @@ -26,6 +26,7 @@ package com.amihaiemil.docker; import java.io.IOException; +import java.util.Map; import javax.json.JsonObject; /** @@ -69,6 +70,14 @@ Network create(final String name, JsonObject parameters) */ void prune() throws IOException, UnexpectedResponseException; + /** + * Filter these networks. + * @param filters Filters to apply. + * @return Filtered networks. + * @see Docker API Docs + */ + Networks filter(Map> filters); + /** * Return the Docker engine where these Networks came from. * @return Docker. diff --git a/src/main/java/com/amihaiemil/docker/ReadLogString.java b/src/main/java/com/amihaiemil/docker/ReadLogString.java new file mode 100644 index 00000000..9f058639 --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/ReadLogString.java @@ -0,0 +1,228 @@ +/** + * Copyright (c) 2018-2019, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ResponseHandler; +import org.apache.http.entity.ContentType; +import org.apache.http.protocol.HTTP; +import org.apache.http.util.Args; +import org.apache.http.util.CharArrayBuffer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; + +/** + * Handler that returns the response content as a String. + * @author Morozov Evgeniy (lumimul@gmail.com) + * @version $Id$ + * @since 0.0.2 + */ +final class ReadLogString implements ResponseHandler { + + /** + * Handlers to be executed before actually reading the array. + */ + private final ResponseHandler other; + + /** + * Ctor. + * @param other Handlers to be executed before actually reading the array. + */ + ReadLogString(final ResponseHandler other) { + this.other = other; + } + + @Override + public String handleResponse(final HttpResponse httpResponse) + throws IOException { + final HttpResponse resp = this.other.handleResponse(httpResponse); + return this.toString(resp.getEntity()); + } + + /** + * Docker logs contains header + * [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + * STREAM_TYPE + * 0: stdin (is written on stdout) + * 1: stdout + * 2: stderr + * + * SIZE1, SIZE2, SIZE3, SIZE4 are the four bytes of the uint32 size + * encoded as big endian. + * + * This method do: + * + * 1) Read 8 bytes. + * 2) Choose stdout or stderr depending on the first byte. + * 3) Extract the frame size from the last four bytes. + * 4) Read the extracted size and output it on the correct output. + * 5) Goto 1. + * + * @param entity HttpEntity for read message. + * @return Logs from container in String. + * @throws IOException if the entity cannot be read + */ + private String toString(final HttpEntity entity) throws IOException { + final InputStream instream = entity.getContent(); + final CharArrayBuffer buffer = new CharArrayBuffer( + this.getCapacity(entity) + ); + if (instream != null) { + try { + final Reader reader = new InputStreamReader( + instream, + this.getCharset(ContentType.get(entity)) + ); + this.read(buffer, reader); + } finally { + instream.close(); + } + } + return buffer.toString(); + } + + /** + * Docker logs contains header + * [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + * STREAM_TYPE + * 0: stdin (is written on stdout) + * 1: stdout + * 2: stderr + * + * SIZE1, SIZE2, SIZE3, SIZE4 are the four bytes of the uint32 size + * encoded as big endian. + * + * 1) Read 8 bytes from reader. + * 2) Choose not stdin(0) depending on the first byte. + * 3) Extract the frame size from the last four bytes. + * 4) Read the extracted size from reader and save it in buffer in circle. + * 5) Goto 1. + * + * @param buffer Buffer for save message. + * @param reader Reader for read message. + * @throws IOException if the entity cannot be read + */ + private void read(final CharArrayBuffer buffer, + final Reader reader) throws IOException { + char[] controlChars = new char[8]; + int len; + while (reader.read(controlChars) != -1) { + if (controlChars[0] != 0) { + long byteInLine = this.getUInt(controlChars); + char[] stdout; + if (byteInLine > 1024) { + stdout = new char[1024]; + } else { + stdout = new char[(int) byteInLine]; + } + while (byteInLine > 0) { + len = reader.read(stdout); + byteInLine -= len; + if (len != -1) { + buffer.append(stdout, 0, len); + } + } + } + } + } + + /** + * Check that content length less then Integer.MAX_VALUE. + * Try to get content length from entity + * If length less then zero return 4096 + * + * @param entity HttpEntity for get capacity. + * @return Capacity. + */ + private int getCapacity(final HttpEntity entity) { + Args.check(entity.getContentLength() <= Integer.MAX_VALUE, + "HTTP entity too large to be buffered in memory"); + int capacity = (int) entity.getContentLength(); + if (capacity < 0) { + capacity = 4096; + } + return capacity; + } + + /** + * Try to get charset from content type. + * If charset not set, try get default charset by mime type + * If not set return ISO-8859-1 + * + * @param contentType Content type. + * @return Charset. + */ + private Charset getCharset(final ContentType contentType) { + Charset charset = null; + if (contentType != null) { + charset = contentType.getCharset(); + if (charset == null) { + ContentType defaultContentType = + ContentType.getByMimeType(contentType.getMimeType()); + if (defaultContentType != null) { + charset = defaultContentType.getCharset(); + } else { + charset = null; + } + } + } + + if (charset == null) { + charset = HTTP.DEF_CONTENT_CHARSET; + } + return charset; + } + + /** + * Convert byte to long. + * + * @param byteForConvert Byte for convert. + * @return Long Converted byte. + */ + private long byteAsULong(final char byteForConvert) { + return ((long) byteForConvert) & 0x00000000000000FFL; + } + + /** + * Convert byte from control byte to uint32. + * + * @param bytes Array of byte. + * @return Long uint32. + */ + private long getUInt(final char[] bytes) { + return this.byteAsULong(bytes[7]) + | (this.byteAsULong(bytes[6]) << 8) + | (this.byteAsULong(bytes[5]) << 16) + | (this.byteAsULong(bytes[4]) << 24); + } + + +} diff --git a/src/main/java/com/amihaiemil/docker/RemoteDocker.java b/src/main/java/com/amihaiemil/docker/RemoteDocker.java index 9e003d35..c0053fb9 100644 --- a/src/main/java/com/amihaiemil/docker/RemoteDocker.java +++ b/src/main/java/com/amihaiemil/docker/RemoteDocker.java @@ -32,12 +32,17 @@ /** * Use this to communicate with a remote Docker API. - * + * @deprecated Please use TcpDocker instead. It does + * exactly the same thing, it's just a change for a more suitable + * name. This class will be removed in one of the future releases. + * * @author George Aristy (george.aristy@gmail.com) * @version $Id$ * @since 0.0.1 * @checkstyle ParameterNumber (150 lines) */ + +@Deprecated public final class RemoteDocker extends RtDocker { /** diff --git a/src/main/java/com/amihaiemil/docker/RtContainer.java b/src/main/java/com/amihaiemil/docker/RtContainer.java index cfac3833..dd37ca89 100644 --- a/src/main/java/com/amihaiemil/docker/RtContainer.java +++ b/src/main/java/com/amihaiemil/docker/RtContainer.java @@ -32,6 +32,8 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.message.BasicHeader; /** * Restful Container. @@ -230,4 +232,48 @@ public void unpause() throws IOException { unpause.releaseConnection(); } } + + @Override + public int waitOn(final String state) throws IOException { + UncheckedUriBuilder urib = new UncheckedUriBuilder( + this.baseUri.toString().concat("/wait") + ); + if(!(null == state || state.isEmpty())){ + urib.addParameter("condition", state); + } + + final HttpPost waiter = new HttpPost(urib.build()); + try { + final JsonObject json = this.client.execute( + waiter, + new ReadJsonObject( + new MatchStatus(waiter.getURI(), HttpStatus.SC_OK) + ) + ); + return json.getInt("StatusCode"); + } finally { + waiter.releaseConnection(); + } + } + + @Override + public Exec exec(final JsonObject config) throws IOException { + final URI uri = new UncheckedUriBuilder( + this.baseUri.toString() + "/exec") + .build(); + final HttpPost post = new HttpPost(uri); + try { + post.setEntity(new StringEntity(config.toString())); + post.setHeader(new BasicHeader("Content-Type", "application/json")); + final JsonObject json = this.client.execute( + post, + new ReadJsonObject( + new MatchStatus(post.getURI(), HttpStatus.SC_CREATED) + ) + ); + return this.docker.execs().get(json.getString("Id")); + } finally { + post.releaseConnection(); + } + } } diff --git a/src/main/java/com/amihaiemil/docker/RtContainers.java b/src/main/java/com/amihaiemil/docker/RtContainers.java index 029fee1f..a31bfcc9 100644 --- a/src/main/java/com/amihaiemil/docker/RtContainers.java +++ b/src/main/java/com/amihaiemil/docker/RtContainers.java @@ -57,7 +57,7 @@ abstract class RtContainers implements Containers { * Docker API. */ private final Docker docker; - + /** * Ctor. * @param client Given HTTP Client. @@ -136,7 +136,7 @@ public Container create( post.releaseConnection(); } } - + @Override public Docker docker() { return this.docker; @@ -158,4 +158,24 @@ HttpClient client() { URI baseUri() { return this.baseUri; } + + /** + * Get this container.

+ * + * @param containerId Id of the Container + * @return This container object. + */ + @Override + public Container get(final String containerId) { + return new RtContainer( + Json.createObjectBuilder() + .add("Id", containerId) + .build(), + this.client, + URI.create( + this.baseUri.toString() + "/" + containerId + ), + this.docker + ); + } } diff --git a/src/main/java/com/amihaiemil/docker/RtDocker.java b/src/main/java/com/amihaiemil/docker/RtDocker.java index 650bf735..57f5234b 100644 --- a/src/main/java/com/amihaiemil/docker/RtDocker.java +++ b/src/main/java/com/amihaiemil/docker/RtDocker.java @@ -26,7 +26,6 @@ package com.amihaiemil.docker; import java.io.IOException; -import java.io.Reader; import java.net.URI; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; @@ -50,7 +49,7 @@ abstract class RtDocker implements Docker { * Base URI. */ private final URI baseUri; - + /** * Ctor. * @param client Given HTTP Client. @@ -60,7 +59,7 @@ abstract class RtDocker implements Docker { this.client = client; this.baseUri = baseUri; } - + @Override public final boolean ping() throws IOException { final HttpGet ping = new HttpGet(this.baseUri.toString() + "/_ping"); @@ -70,18 +69,11 @@ public final boolean ping() throws IOException { } @Override - public Reader events() throws IOException, UnexpectedResponseException { - final HttpGet monitor = new HttpGet( - this.baseUri.toString() + "/events" - ); - return this.client.execute( - monitor, - new ReadStream( - new MatchStatus( - monitor.getURI(), - HttpStatus.SC_OK - ) - ) + public Events events() { + return new RtEvents( + this.client, + URI.create(this.baseUri.toString() + "/events"), + this ); } @@ -105,9 +97,10 @@ public final Images images() { @Override public final Networks networks() { - throw new UnsupportedOperationException( - "Networks API is not yet implemented. If you can contribute please," - + " do it here: https://www.github.com/amihaiemil/docker-java-api" + return new ListedNetworks( + this.client, + URI.create(this.baseUri.toString() + "/networks"), + this ); } @@ -121,10 +114,11 @@ public final Volumes volumes() { } @Override - public final Exec exec() { - throw new UnsupportedOperationException( - "Exec API is not yet implemented. If you can contribute please," - + " do it here: https://www.github.com/amihaiemil/docker-java-api" + public final Execs execs() { + return new RtExecs( + this.client, + URI.create(this.baseUri.toString() + "/exec"), + this ); } @@ -132,7 +126,7 @@ public final Exec exec() { public final Swarm swarm() { return new RtSwarm( this.client, - URI.create(this.baseUri.toString().concat("/swarm")), + URI.create(this.baseUri.toString().concat("/swarm")), this ); } @@ -163,10 +157,29 @@ public Version version() throws IOException { final String versionUri = this.baseUri.toString() + "/version"; return new RtVersion( this.client, - URI.create(versionUri) + URI.create(versionUri), + this ); } + @Override + public Info info() throws IOException { + final HttpGet info = new HttpGet(this.baseUri.toString() + "/info"); + try { + return new RtInfo( + this.client.execute( + info, + new ReadJsonObject( + new MatchStatus(info.getURI(), HttpStatus.SC_OK) + ) + ), + this + ); + } finally { + info.releaseConnection(); + } + } + @Override public HttpClient httpClient() { return this.client; diff --git a/src/main/java/com/amihaiemil/docker/RtEvents.java b/src/main/java/com/amihaiemil/docker/RtEvents.java new file mode 100644 index 00000000..bc2d01b1 --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/RtEvents.java @@ -0,0 +1,272 @@ +/** + * Copyright (c) 2018-2020, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.utils.URIBuilder; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * RESTful Events API. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.13 + */ +final class RtEvents implements Events { + /** + * Events filters. + */ + private final Map> filters; + + /** + * Since tim + * estamp. + */ + private final LocalDateTime since; + + /** + * Until timestamp. + */ + private final LocalDateTime until; + + /** + * Apache HttpClient which sends the requests. + */ + private final HttpClient client; + + /** + * Base URI. + */ + private final URI baseUri; + + /** + * Docker API. + */ + private final Docker docker; + + /** + * Ctor. + * @param client Given HTTP Client. + * @param baseUri Base URI, ending with /containers. + * @param dkr Docker where these Containers are from. + */ + RtEvents(final HttpClient client, final URI baseUri, final Docker dkr) { + this(client, baseUri, dkr, new HashMap<>(), null, null); + } + + /** + * Ctor. + * @param client Given HTTP Client. + * @param baseUri Base URI, ending with /containers. + * @param dkr Docker where these Containers are from. + * @param filters Filters to apply on these events. + * @param since Since timestamp. + * @param until Until timestamp. + * @checkstyle ParameterNumber (2 lines) + */ + RtEvents( + final HttpClient client, final URI baseUri, final Docker dkr, + final Map> filters, + final LocalDateTime since, + final LocalDateTime until + ) { + this.client = client; + this.baseUri = baseUri; + this.docker = dkr; + this.filters = filters; + this.since = since; + this.until = until; + } + + @Override + public Events since(final LocalDateTime timestamp) { + return new RtEvents( + this.client, + this.baseUri, + this.docker, + this.filters, + timestamp, + this.until + ); + } + + @Override + public Events until(final LocalDateTime timestamp) { + return new RtEvents( + this.client, + this.baseUri, + this.docker, + this.filters, + this.since, + timestamp + ); + } + + @Override + public Events filter( + final Supplier>> filter + ) { + final Map> merged = new HashMap<>( + this.filters + ); + merged.putAll(filter.get()); + return new RtEvents( + this.client, + this.baseUri, + this.docker, + merged, + this.since, + this.until + ); + } + + /** + * Unlike other methods, we cannot implement this one using Response + * Handlers, because Apache HTTP Client tries to consume the remaining + * content after all the handlers have been executed, which results in + * a blockage, since the underlying InputStream is potentially infinite. + * + * @checkstyle CommentsIndentation (100 lines) + * @checkstyle Indentation (100 lines) + * @return Stream of Events. + * @throws IOException If any I/O problem occurs. + * @throws UnexpectedResponseException If the response status is not 200. + */ + @Override + public Stream monitor() + throws IOException, UnexpectedResponseException { + final HttpGet monitor = new HttpGet(this.buildMonitorUri()); + final HttpResponse response = this.client.execute(monitor); + final int actual = response.getStatusLine().getStatusCode(); + if(actual != HttpStatus.SC_OK) { + throw new UnexpectedResponseException( + this.buildMonitorUri().toString(), + actual, HttpStatus.SC_OK, + Json.createObjectBuilder().build() + ); + } else { + final InputStream content = response.getEntity().getContent(); + final Stream stream = Stream.generate( + () -> { + JsonObject read = null; + try { + final byte[] tmp = new byte[4096]; + while (content.read(tmp) != -1) { + try { + final JsonReader reader = Json.createReader( + new ByteArrayInputStream(tmp) + ); + read = reader.readObject(); + break; + //@checkstyle IllegalCatch (1 line) + } catch (final Exception exception) { + //Couldn't parse byte[] to Json, + //try to read more bytes. + } + } + } catch (final IOException ex) { + throw new IllegalStateException( + "IOException when reading streamed JsonObjects!" + ); + } + return read; + } + ).onClose( + () -> { + try { + ((CloseableHttpResponse) response).close(); + } catch (final IOException ex) { + //There is a bug in Apache HTTPClient, when closing + //an infinite InputStream: IOException is thrown + //because the client still tries to read the remainder + // of the closed Stream. We should ignore this case. + } + } + ); + return stream; + } + } + + @Override + public Docker docker() { + return this.docker; + } + + /** + * Build up the URI for the monitoring operation. + * Add the since/until query params if they are present + * and also add the filters. + * @return URI. + */ + private URI buildMonitorUri() { + final URIBuilder uriBuilder = new UncheckedUriBuilder( + this.baseUri.toString() + ); + if (this.since != null) { + uriBuilder.addParameter( + "since", + String.valueOf( + ZonedDateTime.of( + this.since, ZoneId.systemDefault() + ).toInstant().toEpochMilli() + ) + ); + } + if (this.until != null) { + uriBuilder.addParameter( + "until", + String.valueOf( + ZonedDateTime.of( + this.until, ZoneId.systemDefault() + ).toInstant().toEpochMilli() + ) + ); + } + final FilteredUriBuilder uri = new FilteredUriBuilder( + uriBuilder, + this.filters + ); + return uri.build(); + } +} diff --git a/src/main/java/com/amihaiemil/docker/RtExec.java b/src/main/java/com/amihaiemil/docker/RtExec.java new file mode 100644 index 00000000..213e5a67 --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/RtExec.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2018-2020, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import org.apache.http.client.HttpClient; + +import javax.json.JsonObject; +import java.io.IOException; +import java.net.URI; + +/** + * Exec. A batch of commands that are running inside a Container. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.12 + */ +final class RtExec implements Exec { + /** + * Apache HttpClient which sends the requests. + */ + private final HttpClient client; + + /** + * URI of this Exec. + */ + private final URI baseUri; + + /** + * Docker API. + */ + private final Docker docker; + + /** + * Ctor. + * @param client HTTP Client used to send the requests. + * @param uri The URI for this Images API. + * @param dkr The docker entry point. + * @checkstyle ParameterNumber (10 lines) + */ + RtExec(final HttpClient client, final URI uri, final Docker dkr) { + this.client = client; + this.baseUri = uri; + this.docker = dkr; + } + + @Override + public JsonObject inspect() + throws IOException, UnexpectedResponseException { + return new Inspection(this.client, this.baseUri.toString() + "/json"); + } + +} diff --git a/src/main/java/com/amihaiemil/docker/RtExecs.java b/src/main/java/com/amihaiemil/docker/RtExecs.java new file mode 100644 index 00000000..e359b609 --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/RtExecs.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2018-2020, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import org.apache.http.client.HttpClient; + +import java.net.URI; + +/** + * RESTful Execs API. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.12 + */ +final class RtExecs implements Execs { + /** + * Apache HttpClient which sends the requests. + */ + private final HttpClient client; + + /** + * Base URI for the Exec API. + */ + private final URI baseUri; + + /** + * Docker API. + */ + private final Docker docker; + + /** + * Ctor. + * @param client HTTP Client used to send the requests. + * @param uri The URI for this Images API. + * @param dkr The docker entry point. + * @checkstyle ParameterNumber (10 lines) + */ + RtExecs(final HttpClient client, final URI uri, final Docker dkr) { + this.client = client; + this.baseUri = uri; + this.docker = dkr; + } + + @Override + public Exec get(final String execId) { + return new RtExec( + this.client, + URI.create(this.baseUri.toString() + "/" + execId), + this.docker + ); + } + + @Override + public Docker docker() { + return this.docker; + } +} diff --git a/src/main/java/com/amihaiemil/docker/RtInfo.java b/src/main/java/com/amihaiemil/docker/RtInfo.java new file mode 100644 index 00000000..65db46bc --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/RtInfo.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2018-2020, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import javax.json.JsonObject; + +/** + * Info about the Docker engine, in JSON format. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.13 + */ +final class RtInfo extends JsonResource implements Info { + + /** + * Docker to which iot refers to. + */ + private final Docker docker; + + /** + * Constructor. + * @param rep This info in JSON format. + * @param docker The Docker to which it refers to. + */ + RtInfo(final JsonObject rep, final Docker docker) { + super(rep); + this.docker = docker; + } + + @Override + public Docker docker() { + return this.docker; + } +} diff --git a/src/main/java/com/amihaiemil/docker/RtLogs.java b/src/main/java/com/amihaiemil/docker/RtLogs.java index 38da2adc..7b982ab8 100644 --- a/src/main/java/com/amihaiemil/docker/RtLogs.java +++ b/src/main/java/com/amihaiemil/docker/RtLogs.java @@ -110,7 +110,7 @@ public String fetch() throws IOException, UnexpectedResponseException { try { return this.client.execute( fetch, - new ReadString( + new ReadLogString( new MatchStatus( fetch.getURI(), HttpStatus.SC_OK diff --git a/src/main/java/com/amihaiemil/docker/RtVersion.java b/src/main/java/com/amihaiemil/docker/RtVersion.java index fc9718df..42fb3176 100644 --- a/src/main/java/com/amihaiemil/docker/RtVersion.java +++ b/src/main/java/com/amihaiemil/docker/RtVersion.java @@ -39,14 +39,26 @@ * @since 0.0.11 */ final class RtVersion extends JsonResource implements Version { + + /** + * Docker to which this Version belongs. + */ + private final Docker docker; + /** * Ctor. * @param client The http client. * @param uri The URI for this version. + * @param dkr Parent Docker. * @throws IOException If an I/O error occurs. */ - RtVersion(final HttpClient client, final URI uri) throws IOException { + RtVersion( + final HttpClient client, + final URI uri, + final Docker dkr + ) throws IOException { super(fetch(client, uri)); + this.docker = dkr; } /** @@ -134,4 +146,9 @@ public String arch() { public boolean experimental() { return this.getBoolean("Experimental"); } + + @Override + public Docker docker() { + return this.docker; + } } diff --git a/src/main/java/com/amihaiemil/docker/TcpDocker.java b/src/main/java/com/amihaiemil/docker/TcpDocker.java new file mode 100644 index 00000000..8b54980c --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/TcpDocker.java @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2018-2019, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import java.net.URI; +import java.nio.file.Path; + +import org.apache.http.client.HttpClient; + +/** + * Use this to communicate with a remote Docker API. + * + * @author George Aristy (george.aristy@gmail.com) + * @version $Id$ + * @since 0.0.1 + * @checkstyle ParameterNumber (150 lines) + */ +public final class TcpDocker extends RtDocker { + + /** + * Tcp Docker engine. API version is 1.35 by default. + * @param uri Remote Docker URI. + * @param keys Path to the keystore. + * @param trust Path to the truststore. + * @param storePwd Password for the keystore. + * @param keyPwd Passphrase for the key. + */ + TcpDocker( + final URI uri, final Path keys, final Path trust, + final char[] storePwd, final char[] keyPwd) { + this(uri, "v1.35", keys, trust, storePwd, keyPwd); + } + + /** + * Tcp Docker engine. + * @param uri Remote Docker URI. + * @param version API version (eg. v1.35). + * @param keys Path to the keystore. + * @param trust Path to the truststore. + * @param storePwd Password for the keystore. + * @param keyPwd Passphrase for the key. + */ + TcpDocker( + final URI uri, final String version, + final Path keys, final Path trust, + final char[] storePwd, final char[] keyPwd) { + this( + new SslHttpClient(keys, trust, storePwd, keyPwd), + uri, version + ); + } + + /** + * Tcp Docker engine. + * + * An insecure docker API v1.35 endpoint is assumed. + * + * @param uri Remote Docker URI. + */ + public TcpDocker(final URI uri) { + this(new PlainHttpClient(), uri); + } + + /** + * Tcp Docker engine. + * + * An insecure docker API v1.35 endpoint is assumed. + * + * @param uri Remote Docker URI. + * @param auth Remote Docker {@link Auth} + */ + public TcpDocker(final URI uri, final Auth auth) { + this(new AuthHttpClient(new PlainHttpClient(), auth), uri); + } + + /** + * Tcp Docker engine. You have to configure your own HttpClient, + * most likely with some authentication mechanism, depending on where + * the Docker engine is on the Network.

+ * + * By default, the API version is 1.35. + * + * @param client The http client to use. + * @param uri Remote Docker URI. + */ + public TcpDocker(final HttpClient client, final URI uri) { + this(client, uri, "v1.35"); + } + + /** + * Tcp Docker engine. You have to configure your own HttpClient, + * most likely with some authentication mechanism, depending on where + * the Docker engine is on the Network. + * + * @param client The http client to use. + * @param uri Remote Docker URI. + * @param version API version (eg. v1.35). + */ + public TcpDocker( + final HttpClient client, final URI uri, final String version + ) { + super(client, URI.create(uri.toString() + "/" + version)); + } +} diff --git a/src/main/java/com/amihaiemil/docker/UnixDocker.java b/src/main/java/com/amihaiemil/docker/UnixDocker.java new file mode 100644 index 00000000..02f27054 --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/UnixDocker.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2018-2019, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import java.io.File; +import java.net.URI; +import org.apache.http.client.HttpClient; + +/** + * Local Docker API. Use this when you want to communicate with the local + * Docker engine. + * + *
+ *     final Docker docker = new LocalUnixDocker("unix:///var/run/docker.sock");
+ * 
+ * + * This implementation manages an internal pool of 10 http connections. Users + * who wish to alter this behaviour may provide their own {@link HttpClient} + * via the specific constructor. + * + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.1 + */ +public final class UnixDocker extends RtDocker { + + /** + * Unix Docker engine. + * @param unixSocket Unix socket File on disk. + * (most likely /var/run/docker.sock). + */ + public UnixDocker(final File unixSocket){ + this(unixSocket, "v1.35"); + } + + /** + * Unix Docker engine. + * @param unixSocket Unix socket File on disk. + * (most likely /var/run/docker.sock). + * @param version API version (e.g. v1.30). + */ + public UnixDocker(final File unixSocket, final String version){ + this(new UnixHttpClient(unixSocket), version); + } + + /** + * Unix Docker engine. + *

+ * Users may supply their own {@link HttpClient} that must register a + * {@link UnixSocketFactory}. + * @param client The http client to use. + * @param version API version (e.g. v1.30). + */ + public UnixDocker(final HttpClient client, final String version) { + super(client, URI.create("unix://localhost:80/" + version)); + } + +} diff --git a/src/main/java/com/amihaiemil/docker/Version.java b/src/main/java/com/amihaiemil/docker/Version.java index 09601a13..5b6c35eb 100644 --- a/src/main/java/com/amihaiemil/docker/Version.java +++ b/src/main/java/com/amihaiemil/docker/Version.java @@ -49,4 +49,10 @@ public interface Version extends JsonObject { * @return Whether experimental docker features are enabled */ boolean experimental(); + + /** + * The Docker engine to which this Version belongs. + * @return Docker. + */ + Docker docker(); } diff --git a/src/test/java/com/amihaiemil/docker/ListedContainersTestCase.java b/src/test/java/com/amihaiemil/docker/ListedContainersTestCase.java index 29b8d3f7..5ec7d7e6 100644 --- a/src/test/java/com/amihaiemil/docker/ListedContainersTestCase.java +++ b/src/test/java/com/amihaiemil/docker/ListedContainersTestCase.java @@ -49,7 +49,7 @@ public final class ListedContainersTestCase { */ @Test public void iterateAll() { - Docker docker = new LocalDocker( + Docker docker = new UnixDocker( new AssertRequest( new Response( HttpStatus.SC_OK, @@ -116,7 +116,7 @@ public void includeFiltersInRequest() { "randomLabel=test" ) ); - new LocalDocker( + new UnixDocker( new AssertRequest( new Response( HttpStatus.SC_OK, @@ -159,7 +159,7 @@ public void includeAddedFiltersInRequest() { ); final Map> added = new HashMap<>(); added.put("dangling", Collections.singletonList("true")); - new LocalDocker( + new UnixDocker( new AssertRequest( new Response( HttpStatus.SC_OK, diff --git a/src/test/java/com/amihaiemil/docker/ListedNetworksTestCase.java b/src/test/java/com/amihaiemil/docker/ListedNetworksTestCase.java index 61447e52..f84a811a 100644 --- a/src/test/java/com/amihaiemil/docker/ListedNetworksTestCase.java +++ b/src/test/java/com/amihaiemil/docker/ListedNetworksTestCase.java @@ -100,6 +100,51 @@ public void filterResults() throws IOException { ); } + /** + * {@link ListedNetworks} can filter networks + * filters. + */ + @Test + public void filterTest() { + final Map> filters = new HashMap<>(); + filters.put( + "scope", + Arrays.asList( + "local" + ) + ); + MatcherAssert.assertThat( + "Could not filter networks", + new ListedNetworks( + //@checkstyle LineLength (50 lines) + new AssertRequest( + new Response( + HttpStatus.SC_OK, + "[{\"Id\": \"id1\", \"scope\":\"local\"}, {\"Id\":\"cde2\"}]" + ), + new Condition( + // @checkstyle LineLength (11 lines) + "iterate() query parameters must include the filters provided", + req -> { + final List params = new UncheckedUriBuilder( + req.getRequestLine().getUri() + ).getQueryParams(); + // @checkstyle BooleanExpressionComplexity (5 lines) + return params.size() == 1 + && "filters".equals(params.get(0).getName()) + && params.get(0).getValue().contains("scope") + && params.get(0).getValue().contains("local"); + } + ) + ), + URI.create("http://localhost/networks/id1"), + DOCKER + ).filter(filters).iterator().next().getString("Id"), + new IsEqual<>("id1") + ); + + } + /** * {@link ListedNetworks} can iterate over them without * filters. diff --git a/src/test/java/com/amihaiemil/docker/LocalDockerITCase.java b/src/test/java/com/amihaiemil/docker/LocalDockerITCase.java index 4785b2a0..f48bec1d 100644 --- a/src/test/java/com/amihaiemil/docker/LocalDockerITCase.java +++ b/src/test/java/com/amihaiemil/docker/LocalDockerITCase.java @@ -26,19 +26,16 @@ package com.amihaiemil.docker; import java.io.File; -import java.io.Reader; import java.nio.file.Paths; -import org.apache.commons.io.IOUtils; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.hamcrest.collection.IsIterableWithSize; import org.junit.Test; -import org.junit.Ignore; import org.mockito.internal.matchers.GreaterOrEqual; /** - * Integration tests for LocalDocker. + * Integration tests for LocalUnixDocker. * @author Mihai Andronache (amihaiemil@gmail.com) * @version $Id$ * @since 0.0.1 @@ -46,7 +43,7 @@ public final class LocalDockerITCase { /** - * LocalDocker can ping the Docker Engine. + * LocalUnixDocker can ping the Docker Engine. * @throws Exception If something goes wrong. */ @Test @@ -56,25 +53,9 @@ public void pingsDocker() throws Exception { ); MatcherAssert.assertThat(docker.ping(), Matchers.is(Boolean.TRUE)); } + /** - * Docker can follow the events stream. Ignored for now, - * doesn't work yet. - * @throws Exception If something goes wrong. - */ - @Test - @Ignore - public void followsEvents() throws Exception { - final Reader reader = new LocalDocker( - new File("/var/run/docker.sock") - ).events(); - final String events = IOUtils.toString(reader); - MatcherAssert.assertThat( - events.trim(), - Matchers.notNullValue() - ); - } - /** - * LocalDocker can list {@link Volumes}. + * LocalUnixDocker can list {@link Volumes}. * @throws Exception If something goes wrong. */ @Test diff --git a/src/test/java/com/amihaiemil/docker/LocalDockerTestCase.java b/src/test/java/com/amihaiemil/docker/LocalDockerTestCase.java index e4ca6b6e..3663814c 100644 --- a/src/test/java/com/amihaiemil/docker/LocalDockerTestCase.java +++ b/src/test/java/com/amihaiemil/docker/LocalDockerTestCase.java @@ -34,7 +34,7 @@ import org.junit.Test; /** - * Unit tests for LocalDocker. + * Unit tests for LocalUnixDocker. * @author Mihai Andronache (amihaiemil@gmail.com) * @version $Id$ * @since 0.0.1 @@ -42,7 +42,7 @@ public final class LocalDockerTestCase { /** - * LocalDocker can be instantiated. + * LocalUnixDocker can be instantiated. */ @Test public void canBeInstantiate() { @@ -89,7 +89,7 @@ public void pingFalseIfResponseIsNotOk() throws Exception { } /** - * LocalDocker can return the Containers. + * LocalUnixDocker can return the Containers. */ @Test public void getsContainers() { @@ -102,7 +102,7 @@ public void getsContainers() { } /** - * LocalDocker can return the Swarm. + * LocalUnixDocker can return the Swarm. */ @Test public void returnsSwarm() { @@ -115,7 +115,7 @@ public void returnsSwarm() { } /** - * LocalDocker can return Images. + * LocalUnixDocker can return Images. */ @Test public void returnsImages() { @@ -128,7 +128,7 @@ public void returnsImages() { } /** - * LocalDocker can return its HttpClient. + * LocalUnixDocker can return its HttpClient. */ @Test public void returnsHttpClient() { @@ -144,7 +144,7 @@ public void returnsHttpClient() { } /** - * LocalDocker can return Volumes. + * LocalUnixDocker can return Volumes. */ @Test public void returnsVolumes() { @@ -157,27 +157,33 @@ public void returnsVolumes() { } /** - * LocalDocker throws UnsupportedOperationException for Networks. + * LocalUnixDocker can return Networks. */ - @Test(expected = UnsupportedOperationException.class) - public void unsupportedOperationNetworks() { - new LocalDocker( - new File("/var/run/docker.sock") - ).networks(); + @Test + public void returnsNetworks() { + MatcherAssert.assertThat( + new LocalDocker( + new File("/var/run/docker.sock") + ).networks(), + Matchers.notNullValue() + ); } /** - * LocalDocker throws UnsupportedOperationException for Exec. + * LocalUnixDocker can return Execs. */ - @Test(expected = UnsupportedOperationException.class) - public void unsupportedOperationExec() { - new LocalDocker( - new File("/var/run/docker.sock") - ).exec(); + @Test + public void returnsExecs() { + MatcherAssert.assertThat( + new LocalDocker( + new File("/var/run/docker.sock") + ).execs(), + Matchers.notNullValue() + ); } /** - * LocalDocker throws UnsupportedOperationException for Plugins. + * LocalUnixDocker throws UnsupportedOperationException for Plugins. */ @Test(expected = UnsupportedOperationException.class) public void unsupportedOperationPlugins() { diff --git a/src/test/java/com/amihaiemil/docker/RemoteDockerTestCase.java b/src/test/java/com/amihaiemil/docker/RemoteDockerTestCase.java index 5945a375..89294b9b 100644 --- a/src/test/java/com/amihaiemil/docker/RemoteDockerTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RemoteDockerTestCase.java @@ -81,7 +81,7 @@ public void pingFalseIfResponseIsNotOk() throws Exception { } /** - * RemoteDocker can return the Containers. + * RemoteTcpDockercan return the Containers. */ @Test public void getsContainers() { @@ -95,7 +95,7 @@ public void getsContainers() { } /** - * RemoteDocker can return the Swarm. + * RemoteTcpDockercan return the Swarm. */ @Test public void returnsSwarm() { @@ -110,7 +110,7 @@ public void returnsSwarm() { /** - * RemoteDocker can return Images. + * RemoteTcpDockercan return Images. */ @Test public void returnsImages() { @@ -124,7 +124,7 @@ public void returnsImages() { } /** - * LocalDocker can return its HttpClient. + * LocalUnixDocker can return its HttpClient. */ @Test public void returnsHttpClient() { @@ -142,7 +142,7 @@ public void returnsHttpClient() { } /** - * LocalDocker should have AuthHttpClient. + * LocalUnixDocker should have AuthHttpClient. */ @Test public void returnsAuthHttpClient() { @@ -159,7 +159,7 @@ public void returnsAuthHttpClient() { } /** - * RemoteDocker can return Volumes. + * RemoteTcpDockercan return Volumes. */ @Test public void returnsVolumes() { diff --git a/src/test/java/com/amihaiemil/docker/RtContainerITCase.java b/src/test/java/com/amihaiemil/docker/RtContainerITCase.java index a008b7b9..7a23b5b8 100644 --- a/src/test/java/com/amihaiemil/docker/RtContainerITCase.java +++ b/src/test/java/com/amihaiemil/docker/RtContainerITCase.java @@ -49,7 +49,7 @@ public final class RtContainerITCase { */ @Test public void renamesContainer() throws Exception { - final Container container = new LocalDocker( + final Container container = new UnixDocker( new File("/var/run/docker.sock") ).containers().create("Toomes", "hello-world"); MatcherAssert.assertThat( @@ -70,7 +70,7 @@ public void renamesContainer() throws Exception { */ @Test public void startStopContainer() throws Exception { - final Container container = new LocalDocker( + final Container container = new UnixDocker( new File("/var/run/docker.sock") ).containers().create("TestStart", this.containerJsonObject()); container.start(); @@ -88,7 +88,7 @@ public void startStopContainer() throws Exception { */ @Test public void killContainer() throws Exception { - final Container container = new LocalDocker( + final Container container = new UnixDocker( new File("/var/run/docker.sock") ).containers().create("TestKill", this.containerJsonObject()); container.start(); @@ -110,7 +110,8 @@ public void killContainer() throws Exception { */ @Test public void restartContainer() throws Exception { - final Container container = new LocalDocker( + final Container container = new UnixDocker( + new File("/var/run/docker.sock") ).containers().create("TestRestart", this.containerJsonObject()); container.start(); @@ -145,7 +146,7 @@ private JsonObject containerJsonObject() { */ @Test public void pauseContainer() throws Exception { - final Container container = new LocalDocker( + final Container container = new UnixDocker( new File("/var/run/docker.sock") ).containers().create("TestPause", this.containerJsonObject()); container.start(); @@ -164,7 +165,8 @@ public void pauseContainer() throws Exception { */ @Test public void unpauseContainer() throws Exception { - final Container container = new LocalDocker( + final Container container = new UnixDocker( + new File("/var/run/docker.sock") ).containers().create("TestUnpause", this.containerJsonObject()); container.start(); @@ -204,4 +206,37 @@ private boolean pausedState(final Container container) throws IOException { .getBoolean("Paused"); } + + /** + * {@link RtContainer} Can create Exec of Docker container it represents. + * @throws Exception If something goes wrong. + */ + @Test + public void execContainer() throws Exception { + final Container container = new UnixDocker( + new File("/var/run/docker.sock") + ).containers().create("TestStart", this.containerJsonObject()); + container.start(); + MatcherAssert.assertThat( + this.runningState(container), + new IsEqual<>(true) + ); + final JsonObject json = Json.createObjectBuilder() + .add("Cmd", Json.createArrayBuilder().add("date").build()) + .add("Tty", true) + .add("AttachStdout", true) + .build(); + Exec exec = container.exec(json); + MatcherAssert.assertThat( + container.inspect().getJsonArray("ExecIDs").size(), + new IsEqual<>(1) + ); + MatcherAssert.assertThat( + container.inspect().getJsonArray("ExecIDs").getString(0), + new IsEqual<>(exec.inspect().getString("ID")) + ); + container.stop(); + container.remove(); + } + } diff --git a/src/test/java/com/amihaiemil/docker/RtContainerTestCase.java b/src/test/java/com/amihaiemil/docker/RtContainerTestCase.java index 5e1b1ccc..1282d83d 100644 --- a/src/test/java/com/amihaiemil/docker/RtContainerTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RtContainerTestCase.java @@ -36,7 +36,10 @@ import org.mockito.Mockito; import javax.json.Json; import javax.json.JsonObject; +import java.io.IOException; import java.net.URI; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; /** * Unit tests for RtContainer. @@ -627,4 +630,170 @@ public void getsLogs() { Matchers.notNullValue() ); } + + /** + * RtContainer can wait with no problem. + * @throws Exception If something goes wrong. + */ + @Test + public void waitsOk() throws Exception { + int retval = new RtContainer( + Json.createObjectBuilder().add("Id", "123").build(), + new AssertRequest( + new Response( + HttpStatus.SC_OK, + Json.createObjectBuilder() + .add("StatusCode", 0) + .build().toString() + ), + new Condition( + "Method should be a POST", + req -> req.getRequestLine().getMethod().equals("POST") + ), + new Condition( + "Resource path must be /123/wait", + req -> req.getRequestLine().getUri().endsWith("/wait") + ) + ), + URI.create("http://localhost:80/1.30/containers/123"), + Mockito.mock(Docker.class) + ).waitOn(null); + assertEquals(0, retval); + } + + /** + * RtContainer can wait with a condition. + * @throws Exception If something goes wrong. + */ + @Test + public void waitsOkWithCondition() throws Exception { + int retval = new RtContainer( + Json.createObjectBuilder().add("Id", "123").build(), + new AssertRequest( + new Response( + HttpStatus.SC_OK, + Json.createObjectBuilder() + .add("StatusCode", 0) + .build().toString() + ), + new Condition( + "Method should be a POST", + req -> req.getRequestLine().getMethod().equals("POST") + ), + new Condition( + "Resource path must be /123/wait", + req -> req.getRequestLine().getUri().endsWith("ition=next-exit") + ) + ), + URI.create("http://localhost:80/1.30/containers/123"), + Mockito.mock(Docker.class) + ).waitOn("next-exit"); + assertEquals(0, retval); + } + + /** + * RtContainer throws URE if it receives a server error on remove. + * @throws Exception If something goes wrong. + */ + @Test(expected = UnexpectedResponseException.class) + public void waitWithServerError() throws Exception { + new RtContainer( + Json.createObjectBuilder().build(), + new AssertRequest( + new Response( + HttpStatus.SC_INTERNAL_SERVER_ERROR + ) + ), + URI.create("http://localhost:80/1.30/containers/123"), + Mockito.mock(Docker.class) + ).waitOn(null); + } + + /** + * Can create Exec. + * @throws Exception If something goes wrong. + */ + @Test + public void createTest() throws Exception { + final JsonObject json = Json.createObjectBuilder() + .add("Cmd", Json.createArrayBuilder().add("date").build()) + .add("Tty", "true") + .add("AttachStdin", "true") + .build(); + + RtContainer rtContainer = new RtContainer( + Json.createObjectBuilder().add("Id", "123").build(), + new AssertRequest( + new Response( + HttpStatus.SC_CREATED, + Json.createObjectBuilder() + .add("Id", "01e1564097") + .build().toString() + ), + new Condition( + "Method should be a POST", + req -> req.getRequestLine().getMethod().equals("POST") + ), + new Condition( + "Resource path must be /123/exec", + req -> req.getRequestLine().getUri().endsWith("/123/exec") + ) + ), + URI.create("http://localhost:80/1.30/containers/123"), + Mockito.mock(Docker.class) + ); + when(rtContainer.docker().execs()).thenReturn( + new RtExecs( + new AssertRequest( + new Response( + HttpStatus.SC_OK, + "{\"Id\": \"exec123\"}" + ), + new Condition( + "must send a GET request", + req -> "GET".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "resource URL should end with '/exec123/json'", + req -> req.getRequestLine() + .getUri().endsWith("/exec123/json") + ) + ), + URI.create("http://localhost/exec"), + Mockito.mock(Docker.class) + ) + ); + rtContainer.exec(json); + } + + /** + * Must fail if docker responds with error code 500. + * @throws IOException due to code 500 + */ + @Test(expected = UnexpectedResponseException.class) + public void execWithServerError() throws IOException { + final JsonObject json = Json.createObjectBuilder() + .add("Tty", "true") + .add("AttachStdin", "true") + .build(); + + new RtContainer( + Json.createObjectBuilder().add("Id", "123").build(), + new AssertRequest( + new Response( + HttpStatus.SC_INTERNAL_SERVER_ERROR + ), + new Condition( + "Method should be a POST", + req -> req.getRequestLine().getMethod().equals("POST") + ), + new Condition( + "Resource path must be /123/exec", + req -> req.getRequestLine().getUri().endsWith("/123/exec") + ) + ), + URI.create("http://localhost:80/1.30/containers/123"), + Mockito.mock(Docker.class) + ).exec(json); + } } diff --git a/src/test/java/com/amihaiemil/docker/RtContainersITCase.java b/src/test/java/com/amihaiemil/docker/RtContainersITCase.java index 1586c342..0288552f 100644 --- a/src/test/java/com/amihaiemil/docker/RtContainersITCase.java +++ b/src/test/java/com/amihaiemil/docker/RtContainersITCase.java @@ -44,7 +44,7 @@ public final class RtContainersITCase { */ @Test public void iteratesContainers() { - final Containers containers = new LocalDocker( + final Containers containers = new UnixDocker( new File("/var/run/docker.sock") ).containers(); for(final Container container : containers) { diff --git a/src/test/java/com/amihaiemil/docker/RtContainersTestCase.java b/src/test/java/com/amihaiemil/docker/RtContainersTestCase.java index 0a7de5bd..6c87c1dc 100644 --- a/src/test/java/com/amihaiemil/docker/RtContainersTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RtContainersTestCase.java @@ -48,7 +48,7 @@ * @checkstyle MethodName (500 lines) */ public final class RtContainersTestCase { - + /** * RtContainers can return its parent Docker. */ @@ -69,7 +69,7 @@ public void returnsDocker() { Matchers.is(parent) ); } - + /** * Must return the same number of containers as there are elements in the * json array returned by the service. @@ -106,7 +106,7 @@ public void returnsAllContainers() throws Exception { Matchers.is(2) ); } - + /** * Must return the same number of containers as there are elements in the * json array returned by the service. @@ -141,7 +141,7 @@ public void iteratesRunningContainers() throws Exception { Matchers.is(2) ); } - + /** * The iterator works when there are no containers. * @throws Exception If an error occurs. @@ -162,7 +162,7 @@ public void iteratesZeroContainers() throws Exception { Matchers.is(0) ); } - + /** * Must throw {@link UnexpectedResponseException} if response code is 500. * @throws Exception The UnexpectedException. @@ -177,7 +177,7 @@ public void iterateFailsIfResponseIs500() throws Exception { Mockito.mock(Docker.class) ).iterator(); } - + /** * Must throw {@link UnexpectedResponseException} if response code is 400. * @throws Exception The UnexpectedException. @@ -192,7 +192,7 @@ public void iterateFailsIfResponseIs400() throws Exception { Mockito.mock(Docker.class) ).iterator(); } - + /** * The request should be well-formed. * @throws Exception If something goes wrong. @@ -280,7 +280,7 @@ public void createsWith404() throws IOException { ) ), URI.create("http://localhost/test"), - Mockito.mock(Docker.class) + Mockito.mock(Docker.class) ).create("some_image"); } @@ -356,7 +356,7 @@ public void createsWithImageName() throws Exception { ) ), URI.create("http://localhost/test"), - Mockito.mock(Docker.class) + Mockito.mock(Docker.class) ).create("some_name", "some_image"); } @@ -394,7 +394,7 @@ public void createsWithPayloadForCreateJson() throws Exception { ) ), URI.create("http://localhost/test"), - Mockito.mock(Docker.class) + Mockito.mock(Docker.class) ).create(json); } @@ -456,7 +456,7 @@ public void createEscapesNameParameter() throws Exception { ) ), URI.create("http://localhost/docker"), - Mockito.mock(Docker.class) + Mockito.mock(Docker.class) ).create("Adrian Toomes", "some/image"); } @@ -499,7 +499,7 @@ public void createsContainerWithId() throws Exception { ) ), URI.create("http://localhost/test"), - Mockito.mock(Docker.class) + Mockito.mock(Docker.class) ).create( Json.createObjectBuilder() .add("Image", "ubuntu").build() @@ -507,4 +507,25 @@ public void createsContainerWithId() throws Exception { Matchers.is("df2419f4") ); } + + /** + * RtContainers.get() returns an RtContainer with the container id. + * @throws Exception If something goes wrong. + */ + @Test + public void getContainerWithId() throws Exception { + MatcherAssert.assertThat( + new ListedContainers( + new AssertRequest( + new Response( + HttpStatus.SC_OK, + "{ \"Id\": \"df2419f4\", \"Warnings\": [ ] }" + ) + ), + URI.create("http://localhost/test"), + Mockito.mock(Docker.class) + ).get("df2419f4").getString("Id"), + Matchers.is("df2419f4") + ); + } } diff --git a/src/test/java/com/amihaiemil/docker/RtDockerSystemITCase.java b/src/test/java/com/amihaiemil/docker/RtDockerSystemITCase.java index a42a1a1b..5425e9b0 100644 --- a/src/test/java/com/amihaiemil/docker/RtDockerSystemITCase.java +++ b/src/test/java/com/amihaiemil/docker/RtDockerSystemITCase.java @@ -45,7 +45,8 @@ public final class RtDockerSystemITCase { */ @Test public void showDiskSpaceInfo() throws Exception { - final Docker docker = new LocalDocker( + final Docker docker = new UnixDocker( + new File("/var/run/docker.sock") ); DiskSpaceInfo info = docker.system().diskUsage(); diff --git a/src/test/java/com/amihaiemil/docker/RtEventsTestCase.java b/src/test/java/com/amihaiemil/docker/RtEventsTestCase.java new file mode 100644 index 00000000..0c4ada34 --- /dev/null +++ b/src/test/java/com/amihaiemil/docker/RtEventsTestCase.java @@ -0,0 +1,265 @@ +/** + * Copyright (c) 2018-2020, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import com.amihaiemil.docker.mock.AssertRequest; +import com.amihaiemil.docker.mock.Condition; +import com.amihaiemil.docker.mock.Response; +import org.apache.http.HttpStatus; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.json.JsonObject; +import java.net.URI; +import java.net.URLDecoder; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Unit tests for {@link RtEvents}. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.13 + */ +public final class RtEventsTestCase { + + /** + * RtEvents can monitor evens without filters. + * @throws Exception If something goes wrong. + */ + @Test + public void monitorsAll() throws Exception { + final Events all = new RtEvents( + new AssertRequest( + new Response( + HttpStatus.SC_OK, + "{\"id\": \"eventId\"}" + ), + new Condition( + "monitor() must send a GET request", + req -> "GET".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "monitor() resource URL must be '/events'", + req -> req.getRequestLine() + .getUri().endsWith("/events") + ) + ), + URI.create("http://localhost/1.40/events"), + Mockito.mock(Docker.class) + ); + final List events = all.monitor().limit(1) + .collect(Collectors.toList()); + MatcherAssert.assertThat(events, Matchers.iterableWithSize(1)); + MatcherAssert.assertThat( + events.get(0).getString("id"), + Matchers.equalTo("eventId") + ); + } + + /** + * RtEvents can monitor evens with the Since filter. + * @throws Exception If something goes wrong. + */ + @Test + public void monitorsSince() throws Exception { + final LocalDateTime since = LocalDateTime.parse("2020-04-01T00:00"); + final long sinceMillis = ZonedDateTime.of( + since, + ZoneId.systemDefault() + ).toInstant().toEpochMilli(); + final Events all = new RtEvents( + new AssertRequest( + new Response( + HttpStatus.SC_OK, + "{\"id\": \"eventId\"}" + ), + new Condition( + "monitor() must send a GET request", + req -> "GET".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "monitor() resource URL must be '/events?since=...'", + req -> req.getRequestLine() + .getUri().endsWith("/events?since=" + sinceMillis) + ) + ), + URI.create("http://localhost/1.40/events"), + Mockito.mock(Docker.class) + ).since(since); + final List events = all.monitor().limit(1) + .collect(Collectors.toList()); + MatcherAssert.assertThat(events, Matchers.iterableWithSize(1)); + MatcherAssert.assertThat( + events.get(0).getString("id"), + Matchers.equalTo("eventId") + ); + } + + /** + * RtEvents can monitor evens with the Until filter. + * @throws Exception If something goes wrong. + */ + @Test + public void monitorsUntil() throws Exception { + final LocalDateTime until = LocalDateTime.parse("2020-06-01T00:00"); + final long untilMillis = ZonedDateTime.of( + until, + ZoneId.systemDefault() + ).toInstant().toEpochMilli(); + final Events all = new RtEvents( + new AssertRequest( + new Response( + HttpStatus.SC_OK, + "{\"id\": \"eventId\"}" + ), + new Condition( + "monitor() must send a GET request", + req -> "GET".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "monitor() resource URL must be '/events?until=...'", + req -> req.getRequestLine() + .getUri().endsWith("/events?until=" + untilMillis) + ) + ), + URI.create("http://localhost/1.40/events"), + Mockito.mock(Docker.class) + ).until(until); + final List events = all.monitor().limit(1) + .collect(Collectors.toList()); + MatcherAssert.assertThat(events, Matchers.iterableWithSize(1)); + MatcherAssert.assertThat( + events.get(0).getString("id"), + Matchers.equalTo("eventId") + ); + } + + /** + * RtEvents can monitor evens with the a filter map. + * @throws Exception If something goes wrong. + */ + @Test + public void monitorsWithFilter() throws Exception { + final Events all = new RtEvents( + new AssertRequest( + new Response( + HttpStatus.SC_OK, + "{\"id\": \"eventId\"}" + ), + new Condition( + "monitor() must send a GET request", + req -> "GET".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "monitor() resource URL must be '/events?filters=...", + req -> URLDecoder.decode( + req.getRequestLine().getUri() + ).endsWith( + "/events?filters={\"type\":[\"container\"]}" + ) + ) + ), + URI.create("http://localhost/1.40/events"), + Mockito.mock(Docker.class) + ).filter( + () -> { + final Map> filters = new HashMap<>(); + filters.put("type", Arrays.asList("container")); + return filters; + } + ); + final List events = all.monitor().limit(1) + .collect(Collectors.toList()); + MatcherAssert.assertThat(events, Matchers.iterableWithSize(1)); + MatcherAssert.assertThat( + events.get(0).getString("id"), + Matchers.equalTo("eventId") + ); + } + + /** + * RtEvents can monitor evens with the a filter map and also the + * Since/Until flags. + * @throws Exception If something goes wrong. + */ + @Test + public void monitorsWithSinceUntilAndFilters() throws Exception { + final LocalDateTime since = LocalDateTime.parse("2020-04-01T00:00"); + final long sinceMillis = ZonedDateTime.of( + since, + ZoneId.systemDefault() + ).toInstant().toEpochMilli(); + final LocalDateTime until = LocalDateTime.parse("2020-06-01T00:00"); + final long untilMillis = ZonedDateTime.of( + until, + ZoneId.systemDefault() + ).toInstant().toEpochMilli(); + final Events all = new RtEvents( + new AssertRequest( + new Response( + HttpStatus.SC_OK, + "{\"id\": \"eventId\"}" + ), + new Condition( + "monitor() must send a GET request", + req -> "GET".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "monitor() resource URL must be " + + "'/events?since=...&until=...&filters=...", + req -> URLDecoder.decode( + req.getRequestLine().getUri() + ).endsWith( + "/events?since=" + sinceMillis + + "&until=" + untilMillis + + "&filters={\"type\":[\"container\"]}" + ) + ) + ), + URI.create("http://localhost/1.40/events"), + Mockito.mock(Docker.class) + ).since(since).until(until).filter( + () -> { + final Map> filters = new HashMap<>(); + filters.put("type", Arrays.asList("container")); + return filters; + } + ); + final List events = all.monitor().limit(1) + .collect(Collectors.toList()); + MatcherAssert.assertThat(events, Matchers.iterableWithSize(1)); + MatcherAssert.assertThat( + events.get(0).getString("id"), + Matchers.equalTo("eventId") + ); + } +} diff --git a/src/test/java/com/amihaiemil/docker/RtExecsTestCase.java b/src/test/java/com/amihaiemil/docker/RtExecsTestCase.java new file mode 100644 index 00000000..152f974b --- /dev/null +++ b/src/test/java/com/amihaiemil/docker/RtExecsTestCase.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2018-2020, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import com.amihaiemil.docker.mock.AssertRequest; +import com.amihaiemil.docker.mock.Condition; +import com.amihaiemil.docker.mock.Response; +import org.apache.http.HttpStatus; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.json.Json; +import java.net.URI; + +/** + * Unit tests for {@link RtExecs}. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.12 + */ +public final class RtExecsTestCase { + + /** + * An Exec can return its JSON inspection. + * @throws Exception If something goes wrong. + */ + @Test + public void execReturnsItsInspection() throws Exception { + final Execs all = new RtExecs( + new AssertRequest( + new Response( + HttpStatus.SC_OK, + "{\"Id\": \"exec123\"}" + ), + new Condition( + "inspect() must send a GET request", + req -> "GET".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "inspect() resource URL should end with '/exec123/json'", + req -> req.getRequestLine() + .getUri().endsWith("/exec123/json") + ) + ), + URI.create("http://localhost/exec"), + Mockito.mock(Docker.class) + ); + final Exec exec = all.get("exec123"); + MatcherAssert.assertThat(exec, Matchers.notNullValue()); + MatcherAssert.assertThat( + exec.inspect(), + Matchers.equalTo( + Json.createObjectBuilder() + .add("Id", "exec123") + .build() + ) + ); + } + +} diff --git a/src/test/java/com/amihaiemil/docker/RtImageITCase.java b/src/test/java/com/amihaiemil/docker/RtImageITCase.java index 01e9b2d7..8158ce9c 100644 --- a/src/test/java/com/amihaiemil/docker/RtImageITCase.java +++ b/src/test/java/com/amihaiemil/docker/RtImageITCase.java @@ -45,7 +45,7 @@ public final class RtImageITCase { */ @Test public void returnsHistory() throws Exception { - final Image img = new LocalDocker( + final Image img = new UnixDocker( new File("/var/run/docker.sock") ).images().pull("hello-world", "latest"); for(final Image parent:img.history()) { diff --git a/src/test/java/com/amihaiemil/docker/RtImagesITCase.java b/src/test/java/com/amihaiemil/docker/RtImagesITCase.java index 457b10d2..e7c9b0a1 100644 --- a/src/test/java/com/amihaiemil/docker/RtImagesITCase.java +++ b/src/test/java/com/amihaiemil/docker/RtImagesITCase.java @@ -49,7 +49,7 @@ public final class RtImagesITCase { */ @Test public void iteratesImages() throws Exception { - final Images images = new LocalDocker( + final Images images = new UnixDocker( new File("/var/run/docker.sock") ).images(); for(final Image img : images) { @@ -80,7 +80,8 @@ public void filterImage() throws Exception { "ubuntu" ) ); - final Images images = new LocalDocker( + + final Images images = new UnixDocker( new File("/var/run/docker.sock") ).images(); Images filtered = images.filter(filters); diff --git a/src/test/java/com/amihaiemil/docker/RtLogsITCase.java b/src/test/java/com/amihaiemil/docker/RtLogsITCase.java index 98c484fc..61bf95d5 100644 --- a/src/test/java/com/amihaiemil/docker/RtLogsITCase.java +++ b/src/test/java/com/amihaiemil/docker/RtLogsITCase.java @@ -51,13 +51,13 @@ public final class RtLogsITCase { */ @Test public void fetchesLogs() throws Exception { - final Container container = new LocalDocker( + final Container container = new UnixDocker( new File("/var/run/docker.sock") ).images().pull("hello-world", "latest").run(); final String logs = container.logs().fetch(); MatcherAssert.assertThat( - logs.trim(), - new StringStartsWith("Hello from Docker!") + logs, + new StringStartsWith("\nHello from Docker!") ); } @@ -68,7 +68,7 @@ public void fetchesLogs() throws Exception { @Test @Ignore public void followsLogs() throws Exception { - final Container container = new LocalDocker( + final Container container = new UnixDocker( new File("/var/run/docker.sock") ).images().pull("ubuntu", "latest").run(); final String logs = IOUtils.toString(container.logs().follow()); diff --git a/src/test/java/com/amihaiemil/docker/RtLogsTestCase.java b/src/test/java/com/amihaiemil/docker/RtLogsTestCase.java index 4ac3d27f..a3e6c97a 100644 --- a/src/test/java/com/amihaiemil/docker/RtLogsTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RtLogsTestCase.java @@ -99,7 +99,7 @@ public void followsLogs() throws Exception { ); } } - + /** * RtLogs can fetch the Container's logs (return them as a String). * @throws Exception If something goes wrong. @@ -109,12 +109,8 @@ public void fetchesLogs() throws Exception { final Logs logs = new RtLogs( Mockito.mock(Container.class), new AssertRequest( - new Response( - HttpStatus.SC_OK, - Json.createObjectBuilder() - .add("logs", "...fetched logs...") - .build().toString() - ), + new Response(HttpStatus.SC_OK, + this.prepareMessage("...fetched logs...")), new Condition( "Method should be a GET", req -> req.getRequestLine().getMethod().equals("GET") @@ -130,10 +126,37 @@ public void fetchesLogs() throws Exception { ); MatcherAssert.assertThat( logs.fetch(), - Matchers.equalTo("{\"logs\":\"...fetched logs...\"}") + Matchers.equalTo("...fetched logs...") ); } - + + /** + * Docker logs contains header - + * header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + * STREAM_TYPE + * 0: stdin (is written on stdout) + * 1: stdout + * 2: stderr + * + * SIZE1, SIZE2, SIZE3, SIZE4 are the four bytes of the uint32 size + * encoded as big endian. + * + * @param message Message from container + * @return String with header. + */ + private String prepareMessage(final String message) { + char[] chars = new char[8]; + chars[0] = 1; + chars[1] = 0; + chars[2] = 0; + chars[3] = 0; + chars[4] = 0; + chars[5] = 0; + chars[6] = 0; + chars[7] = (char) message.length(); + return new String(chars) + message; + } + /** * RtLogs.toString() fetches the logs as String. */ @@ -144,9 +167,7 @@ public void toStringFetch() { new AssertRequest( new Response( HttpStatus.SC_OK, - Json.createObjectBuilder() - .add("logs", "toString logs") - .build().toString() + this.prepareMessage("toString logs") ), new Condition( "Method should be a GET", @@ -163,7 +184,7 @@ public void toStringFetch() { ); MatcherAssert.assertThat( logs.toString(), - Matchers.equalTo("{\"logs\":\"toString logs\"}") + Matchers.equalTo("toString logs") ); } @@ -178,9 +199,7 @@ public void logStdout() throws Exception { new AssertRequest( new Response( HttpStatus.SC_OK, - Json.createObjectBuilder() - .add("logs", "stdout logs") - .build().toString() + this.prepareMessage("stdout logs") ), new Condition( "Method should be a GET", @@ -197,7 +216,7 @@ public void logStdout() throws Exception { ); MatcherAssert.assertThat( logs.stdout().fetch(), - Matchers.equalTo("{\"logs\":\"stdout logs\"}") + Matchers.equalTo("stdout logs") ); } @@ -212,9 +231,7 @@ public void logStderr() throws Exception { new AssertRequest( new Response( HttpStatus.SC_OK, - Json.createObjectBuilder() - .add("logs", "stderr logs") - .build().toString() + this.prepareMessage("stderr logs") ), new Condition( "Method should be a GET", @@ -231,7 +248,7 @@ public void logStderr() throws Exception { ); MatcherAssert.assertThat( logs.stderr().fetch(), - Matchers.equalTo("{\"logs\":\"stderr logs\"}") + Matchers.equalTo("stderr logs") ); } diff --git a/src/test/java/com/amihaiemil/docker/RtVersionTestCase.java b/src/test/java/com/amihaiemil/docker/RtVersionTestCase.java index a69df7bf..283faa9b 100644 --- a/src/test/java/com/amihaiemil/docker/RtVersionTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RtVersionTestCase.java @@ -45,7 +45,9 @@ public final void queryDockerVersion() throws IOException { return ((ReadJsonObject) invocation.getArguments()[1]) .handleResponse(response); }); + Docker docker = new LocalDocker(client, "v1.35"); + Version version = docker.version(); assertEquals("19.03.3", version.version()); assertEquals("Docker Engine - Community", version.platformName()); diff --git a/src/test/java/com/amihaiemil/docker/TcpDockerTestCase.java b/src/test/java/com/amihaiemil/docker/TcpDockerTestCase.java new file mode 100644 index 00000000..7df3a819 --- /dev/null +++ b/src/test/java/com/amihaiemil/docker/TcpDockerTestCase.java @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2018-2019, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import com.amihaiemil.docker.mock.AssertRequest; +import com.amihaiemil.docker.mock.Response; + +import java.net.URI; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.mockito.Mockito; + +/** + * Unit tests for {@link TcpDocker}. + * @author George Aristy (george.aristy@gmail.com) + * @version $Id$ + * @since 0.0.1 + * @checkstyle MethodName (500 lines) + */ + +public final class TcpDockerTestCase { + + /** + * Ping must be TRUE if response is OK. + * @throws Exception If an error occurs. + */ + @Test + public void pingTrueIfResponseIsOk() throws Exception { + MatcherAssert.assertThat( + new TcpDocker( + new AssertRequest( + new Response(HttpStatus.SC_OK, "") + ), + URI.create("http://remotedocker") + ).ping(), + Matchers.is(true) + ); + } + + /** + * Ping must be False if response is not OK. + * @throws Exception If an error occurs. + */ + @Test + public void pingFalseIfResponseIsNotOk() throws Exception { + MatcherAssert.assertThat( + new TcpDocker( + new AssertRequest( + new Response(HttpStatus.SC_NOT_FOUND, "") + ), + URI.create("http://remotedocker") + ).ping(), + Matchers.is(false) + ); + } + + /** + * TcpDocker can return the Containers. + */ + @Test + public void getsContainers() { + MatcherAssert.assertThat( + new TcpDocker( + Mockito.mock(HttpClient.class), + URI.create("http://localhost") + ).containers(), + Matchers.notNullValue() + ); + } + + /** + * TcpDocker can return the Swarm. + */ + @Test + public void returnsSwarm() { + MatcherAssert.assertThat( + new TcpDocker( + Mockito.mock(HttpClient.class), + URI.create("http://localhost") + ).swarm(), + Matchers.notNullValue() + ); + } + + + /** + * TcpDocker can return Images. + */ + @Test + public void returnsImages() { + MatcherAssert.assertThat( + new TcpDocker( + Mockito.mock(HttpClient.class), + URI.create("http://localhost") + ).images(), + Matchers.notNullValue() + ); + } + + /** + * UnixDocker can return its HttpClient. + */ + @Test + public void returnsHttpClient() { + final HttpClient client = Mockito.mock(HttpClient.class); + MatcherAssert.assertThat( + new TcpDocker( + client, + URI.create("http://localhost") + ).httpClient(), + Matchers.allOf( + Matchers.notNullValue(), + Matchers.sameInstance(client) + ) + ); + } + + /** + * UnixDocker should have AuthHttpClient. + */ + @Test + public void returnsAuthHttpClient() { + MatcherAssert.assertThat( + new TcpDocker( + URI.create("http://localhost"), + new Credentials("user", "pwd", "user@email.com", "server.com") + ).httpClient(), + Matchers.allOf( + Matchers.notNullValue(), + Matchers.instanceOf(AuthHttpClient.class) + ) + ); + } + + /** + * TcpDocker can return Volumes. + */ + @Test + public void returnsVolumes() { + MatcherAssert.assertThat( + new TcpDocker( + Mockito.mock(HttpClient.class), + URI.create("http://localhost") + ).volumes(), + Matchers.notNullValue() + ); + } + + /** + * TcpDocker can return Networks. + */ + @Test + public void returnsNetworks() { + MatcherAssert.assertThat( + new TcpDocker( + Mockito.mock(HttpClient.class), + URI.create("http://localhost") + ).networks(), + Matchers.notNullValue() + ); + } +} diff --git a/src/test/java/com/amihaiemil/docker/UnixDockerITCase.java b/src/test/java/com/amihaiemil/docker/UnixDockerITCase.java new file mode 100644 index 00000000..da348fa7 --- /dev/null +++ b/src/test/java/com/amihaiemil/docker/UnixDockerITCase.java @@ -0,0 +1,262 @@ +/** + * Copyright (c) 2018-2019, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.hamcrest.collection.IsIterableWithSize; +import org.junit.Test; +import org.mockito.internal.matchers.GreaterOrEqual; + +import javax.json.JsonObject; + +/** + * Integration tests for LocalUnixDocker. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.1 + */ +public final class UnixDockerITCase { + + /** + * UnixDocker can ping the Docker Engine. + * @throws Exception If something goes wrong. + */ + @Test + public void pingsDocker() throws Exception { + final Docker docker = new UnixDocker( + new File("/var/run/docker.sock") + ); + MatcherAssert.assertThat(docker.ping(), Matchers.is(Boolean.TRUE)); + } + + /** + * UnixDocker can return Info about Docker. + * @throws Exception If something goes wrong. + */ + @Test + public void returnsInfo() throws Exception { + final Docker docker = new UnixDocker( + new File("/var/run/docker.sock") + ); + final Info info = docker.info(); + MatcherAssert.assertThat(info, Matchers.notNullValue()); + MatcherAssert.assertThat(info.docker(), Matchers.is(docker)); + MatcherAssert.assertThat( + info.getString("OSType"), + Matchers.equalTo("linux") + ); + } + + /** + * UnixDocker can return the Version of Docker. + * @throws Exception If something goes wrong. + */ + @Test + public void returnsVersion() throws Exception { + final Docker docker = new UnixDocker( + new File("/var/run/docker.sock") + ); + final Version version = docker.version(); + MatcherAssert.assertThat(version, Matchers.notNullValue()); + MatcherAssert.assertThat(version.docker(), Matchers.is(docker)); + MatcherAssert.assertThat( + version.platformName(), + Matchers.equalTo("Docker Engine - Community") + ); + } + + + /** + * Docker can return its Events unfiltered. + * @throws Exception If something goes wrong. + */ + @Test + public void returnsUnfilteredEvents() throws Exception { + final Thread pull = new Thread( + () -> { + try { + Thread.sleep(2000); + final Image img = new UnixDocker( + new File("/var/run/docker.sock") + ).images().pull("hello-world", "latest"); + } catch (final IOException | InterruptedException ex) { + throw new IllegalStateException(ex); + } + } + ); + pull.start(); + try { + final Events events = new UnixDocker( + new File("/var/run/docker.sock") + ).events(); + final JsonObject pulled = events + .monitor() + .limit(1) + .collect(Collectors.toList()) + .get(0); + MatcherAssert.assertThat(pulled, Matchers.notNullValue()); + } finally { + pull.stop(); + } + } + + /** + * Docker can return its Events with filtering at streaming time. + * @throws Exception If something goes wrong. + */ + @Test + public void returnsFilteredStreamOfEvents() throws Exception { + final Thread pull = new Thread( + () -> { + try { + Thread.sleep(2000); + final Images images = new UnixDocker( + new File("/var/run/docker.sock") + ).images(); + images.pull("hello-world", "latest"); + images.pull("ubuntu", "latest"); + images.pull("hello-world", "latest"); + images.pull("ubuntu", "latest"); + } catch (final IOException | InterruptedException ex) { + throw new IllegalStateException(ex); + } + } + ); + pull.start(); + try { + final Events events = new UnixDocker( + new File("/var/run/docker.sock") + ).events(); + final List pulled = events + .monitor() + .filter(json -> json.getString("id").startsWith("ubuntu")) + .limit(2) + .collect(Collectors.toList()); + MatcherAssert.assertThat( + pulled, + Matchers.iterableWithSize(2) + ); + MatcherAssert.assertThat( + pulled.get(0).getString("id"), + Matchers.equalTo("ubuntu:latest") + ); + MatcherAssert.assertThat( + pulled.get(1).getString("id"), + Matchers.equalTo("ubuntu:latest") + ); + } finally { + pull.stop(); + } + } + + /** + * Docker can return its Events which are filter at request time. + * @checkstyle LineLength (100 lines) + * @throws Exception If something goes wrong. + */ + @Test + public void returnsFilteredEvents() throws Exception { + final Thread pull = new Thread( + () -> { + try { + Thread.sleep(2000); + final Container started = new UnixDocker( + new File("/var/run/docker.sock") + ).images().pull("hello-world", "latest").run(); + } catch (final IOException | InterruptedException ex) { + throw new IllegalStateException(ex); + } + } + ); + pull.start(); + try { + final Events events = new UnixDocker( + new File("/var/run/docker.sock") + ).events(); + final List streamed = events + .filter( + () -> { + final Map> filters = new HashMap<>(); + filters.put("type", Arrays.asList("container")); + return filters; + } + ) + .monitor() + .limit(3) + .collect(Collectors.toList()); + MatcherAssert.assertThat( + streamed, + Matchers.iterableWithSize(3) + ); + for(final JsonObject event : streamed) { + MatcherAssert.assertThat( + event.getString("Type"), + Matchers.equalTo("container") + ); + } + MatcherAssert.assertThat( + streamed.get(0).getString("status"), + Matchers.equalTo("create") + ); + MatcherAssert.assertThat( + streamed.get(1).getString("status"), + Matchers.equalTo("start") + ); + MatcherAssert.assertThat( + streamed.get(2).getString("status"), + Matchers.equalTo("die") + ); + } finally { + pull.stop(); + } + } + + /** + * UnixDocker can list {@link Volumes}. + * @throws Exception If something goes wrong. + */ + @Test + public void listVolumes() throws Exception { + final Docker docker = new UnixDocker( + Paths.get("/var/run/docker.sock").toFile() + ); + MatcherAssert.assertThat( + docker.volumes(), + new IsIterableWithSize<>(new GreaterOrEqual<>(0)) + ); + } + +} diff --git a/src/test/java/com/amihaiemil/docker/UnixDockerTestCase.java b/src/test/java/com/amihaiemil/docker/UnixDockerTestCase.java new file mode 100644 index 00000000..b21cb98f --- /dev/null +++ b/src/test/java/com/amihaiemil/docker/UnixDockerTestCase.java @@ -0,0 +1,231 @@ +/** + * Copyright (c) 2018-2019, Mihai Emil Andronache + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1)Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2)Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3)Neither the name of docker-java-api nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.amihaiemil.docker; + +import com.amihaiemil.docker.mock.AssertRequest; +import com.amihaiemil.docker.mock.Condition; +import com.amihaiemil.docker.mock.Response; +import java.io.File; +import org.apache.http.HttpStatus; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +import javax.json.Json; + +/** + * Unit tests for LocalUnixDocker. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.1 + */ +public final class UnixDockerTestCase { + + /** + * UnixDocker can be instantiated. + */ + @Test + public void canBeInstantiate() { + MatcherAssert.assertThat( + new UnixDocker( + new File("/var/run/docker.sock") + ), + Matchers.notNullValue() + ); + } + + /** + * Ping must be TRUE if response is OK. + * @throws Exception If an error occurs. + */ + @Test + public void pingTrueIfResponseIsOk() throws Exception { + MatcherAssert.assertThat( + new UnixDocker( + new AssertRequest( + new Response(HttpStatus.SC_OK, "") + ), + "v1.35" + ).ping(), + Matchers.is(true) + ); + } + + /** + * Ping must be False if response is not OK. + * @throws Exception If an error occurs. + */ + @Test + public void pingFalseIfResponseIsNotOk() throws Exception { + MatcherAssert.assertThat( + new UnixDocker( + new AssertRequest( + new Response(HttpStatus.SC_NOT_FOUND, "") + ), + "v1.35" + ).ping(), + Matchers.is(false) + ); + } + + /** + * UnixDocker can return the Containers. + */ + @Test + public void getsContainers() { + MatcherAssert.assertThat( + new UnixDocker( + new File("/var/run/docker.sock") + ).containers(), + Matchers.notNullValue() + ); + } + + /** + * UnixDocker can return the Swarm. + */ + @Test + public void returnsSwarm() { + MatcherAssert.assertThat( + new UnixDocker( + new File("/var/run/docker.sock") + ).swarm(), + Matchers.notNullValue() + ); + } + + /** + * UnixDocker can return Images. + */ + @Test + public void returnsImages() { + MatcherAssert.assertThat( + new UnixDocker( + new File("/var/run/docker.sock") + ).images(), + Matchers.notNullValue() + ); + } + + /** + * UnixDocker can return its HttpClient. + */ + @Test + public void returnsHttpClient() { + MatcherAssert.assertThat( + new UnixDocker( + new File("/var/run/docker.sock") + ).httpClient(), + Matchers.allOf( + Matchers.notNullValue(), + Matchers.instanceOf(UnixHttpClient.class) + ) + ); + } + + /** + * UnixDocker can return Volumes. + */ + @Test + public void returnsVolumes() { + MatcherAssert.assertThat( + new UnixDocker( + new File("/var/run/docker.sock") + ).volumes(), + Matchers.notNullValue() + ); + } + + /** + * UnixDocker can return Networks. + */ + @Test + public void returnsNetworks() { + MatcherAssert.assertThat( + new UnixDocker( + new File("/var/run/docker.sock") + ).networks(), + Matchers.notNullValue() + ); + } + + /** + * UnixDocker can return Execs. + */ + @Test + public void returnsExecs() { + MatcherAssert.assertThat( + new UnixDocker( + new File("/var/run/docker.sock") + ).execs(), + Matchers.notNullValue() + ); + } + + /** + * UnixDocker throws UnsupportedOperationException for Plugins. + */ + @Test(expected = UnsupportedOperationException.class) + public void unsupportedOperationPlugins() { + new UnixDocker( + new File("/var/run/docker.sock") + ).plugins(); + } + + /** + * RtDocker can return info about itself. + * @throws Exception If something goes wrong. + */ + @Test + public void returnsInfo() throws Exception { + final Docker docker = new UnixDocker( + new AssertRequest( + new Response( + HttpStatus.SC_OK, + "{\"info\": \"running\"}" + ), + new Condition( + "info() must send a GET request", + req -> "GET".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "info() resource URL must be unix://localhost:80/1.40/info", + req -> req.getRequestLine() + .getUri().equals("unix://localhost:80/1.40/info") + ) + ), + "1.40" + ); + MatcherAssert.assertThat( + docker.info(), + Matchers.equalTo( + Json.createObjectBuilder() + .add("info", "running") + .build() + ) + ); + } +}