diff --git a/src/main/java/com/amihaiemil/docker/ReadJsonArray.java b/src/main/java/com/amihaiemil/docker/ReadJsonArray.java index bee944a0..dc6bc3e0 100644 --- a/src/main/java/com/amihaiemil/docker/ReadJsonArray.java +++ b/src/main/java/com/amihaiemil/docker/ReadJsonArray.java @@ -35,7 +35,7 @@ * Handler that reads a JsonArray from the response. * @author Mihai Andronache (amihaiemil@gmail.com) * @version $Id$ - * @since 0.0. + * @since 0.0.1 * @todo #84:30min Write some unit tests for this class, it is * currently only tested by RtImagesITCase#iteratesImages. */ diff --git a/src/main/java/com/amihaiemil/docker/ReadJsonObject.java b/src/main/java/com/amihaiemil/docker/ReadJsonObject.java index b1814051..281f219e 100644 --- a/src/main/java/com/amihaiemil/docker/ReadJsonObject.java +++ b/src/main/java/com/amihaiemil/docker/ReadJsonObject.java @@ -35,7 +35,7 @@ * Handler that reads a JsonObject from the response. * @author Mihai Andronache (amihaiemil@gmail.com) * @version $Id$ - * @since 0.0. + * @since 0.0.1 */ final class ReadJsonObject implements ResponseHandler { diff --git a/src/main/java/com/amihaiemil/docker/ReadLogsStream.java b/src/main/java/com/amihaiemil/docker/ReadLogsStream.java new file mode 100644 index 00000000..d3b1df94 --- /dev/null +++ b/src/main/java/com/amihaiemil/docker/ReadLogsStream.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2018, 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.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import org.apache.http.HttpResponse; +import org.apache.http.client.ResponseHandler; + +/** + * Handler that returns the stream response as a Reader. + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.2 + */ +final class ReadLogsStream 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. + */ + ReadLogsStream(final ResponseHandler other) { + this.other = other; + } + + @Override + public Reader handleResponse(final HttpResponse httpResponse) + throws IOException { + final HttpResponse resp = this.other.handleResponse(httpResponse); + return new InputStreamReader(resp.getEntity().getContent()); + } +} diff --git a/src/main/java/com/amihaiemil/docker/RtLogs.java b/src/main/java/com/amihaiemil/docker/RtLogs.java index cb9fd902..c8b7520e 100644 --- a/src/main/java/com/amihaiemil/docker/RtLogs.java +++ b/src/main/java/com/amihaiemil/docker/RtLogs.java @@ -28,16 +28,22 @@ import java.io.IOException; import java.io.Reader; import java.net.URI; +import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; /** * Restful container logs. * @author Mihai Andronache (amihaiemil@gmail.com) * @version $Id$ * @since 0.0.2 - * @todo #128:30min Continue implementing this class (fetch and follow) and also + * @todo #130:30min Continue implementing this class (method fetch) and also * take into consideration the query parameters as described here: * https://docs.docker.com/engine/api/v1.35/#operation/ContainerLogs + * @todo #130:30min Write some ITCase for the fetch() method. We might have to + * implement the stream decoding (in case TTY is disabled when the Container + * is created), as explained here, in "Stream format" paragraph: + * https://docs.docker.com/engine/api/v1.37/#operation/ContainerAttach */ final class RtLogs implements Logs { @@ -79,9 +85,19 @@ public String fetch() throws IOException, UnexpectedResponseException { @Override public Reader follow() throws IOException, UnexpectedResponseException { - throw new UnsupportedOperationException( - "Operation not yet implemented. If you can contribute please," - + " do it here: https://www.github.com/amihaiemil/docker-java-api" + final HttpGet follow = new HttpGet( + new UncheckedUriBuilder(this.baseUri.toString()) + .addParameter("follow", "true") + .build() + ); + return this.client.execute( + follow, + new ReadLogsStream( + new MatchStatus( + follow.getURI(), + HttpStatus.SC_SWITCHING_PROTOCOLS + ) + ) ); } diff --git a/src/test/java/com/amihaiemil/docker/RtLogsTestCase.java b/src/test/java/com/amihaiemil/docker/RtLogsTestCase.java index 035e44be..ff8c49a5 100644 --- a/src/test/java/com/amihaiemil/docker/RtLogsTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RtLogsTestCase.java @@ -25,7 +25,14 @@ */ 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.BufferedReader; import java.net.URI; +import java.util.stream.Collectors; +import javax.json.Json; +import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; @@ -57,4 +64,40 @@ public void getsContainer(){ ); } + /** + * RtLogs can follow the Container's logs (return a Reader of the stream). + * @throws Exception If something goes wrong. + */ + @Test + public void followsLogs() throws Exception { + final Logs logs = new RtLogs( + Mockito.mock(Container.class), + new AssertRequest( + new Response( + HttpStatus.SC_SWITCHING_PROTOCOLS, + Json.createObjectBuilder() + .add("logs", "...some logs...") + .build().toString() + ), + new Condition( + "Method should be a GET", + req -> req.getRequestLine().getMethod().equals("GET") + ), + new Condition( + "Resource path must be /123/logs?follow=true", + req -> req.getRequestLine().getUri().endsWith( + "/123/logs?follow=true" + ) + ) + ), + URI.create("http://localhost:80/1.30/containers/123/logs") + ); + try (final BufferedReader bfr = new BufferedReader(logs.follow());) { + MatcherAssert.assertThat( + bfr.lines().collect(Collectors.joining("\n")), + Matchers.equalTo("{\"logs\":\"...some logs...\"}") + ); + } + } + }