From d6028813b77b2344ea877809862a220ba41b37df Mon Sep 17 00:00:00 2001 From: Boris Kuzmic Date: Mon, 10 Dec 2018 15:14:37 +0100 Subject: [PATCH 1/3] Adding Volume delete and create methods --- .../java/com/amihaiemil/docker/RtVolume.java | 27 +++++- .../java/com/amihaiemil/docker/RtVolumes.java | 31 +++++++ .../java/com/amihaiemil/docker/Volume.java | 13 ++- .../java/com/amihaiemil/docker/Volumes.java | 14 +++ .../docker/ListedVolumesTestCase.java | 1 - .../amihaiemil/docker/RtVolumeTestCase.java | 93 ++++++++++++++++++- .../amihaiemil/docker/RtVolumesTestCase.java | 42 +++++++++ 7 files changed, 211 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/amihaiemil/docker/RtVolume.java b/src/main/java/com/amihaiemil/docker/RtVolume.java index 0ab93e0f..a5afc409 100644 --- a/src/main/java/com/amihaiemil/docker/RtVolume.java +++ b/src/main/java/com/amihaiemil/docker/RtVolume.java @@ -1,10 +1,11 @@ package com.amihaiemil.docker; -import org.apache.http.client.HttpClient; - -import javax.json.JsonObject; import java.io.IOException; import java.net.URI; +import javax.json.JsonObject; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpDelete; /** * Runtime {@link Volume}. @@ -51,4 +52,24 @@ public JsonObject inspect() throws IOException, UnexpectedResponseException { return new Inspection(this.client, this.baseUri.toString()); } + + @Override + public void delete(final Boolean force) + throws IOException, UnexpectedResponseException { + final UncheckedUriBuilder uri = new UncheckedUriBuilder( + this.baseUri.toString() + ); + uri.addParameter("force", force.toString()); + final HttpDelete delete = new HttpDelete( + uri.build() + ); + try { + this.client.execute( + delete, + new MatchStatus(delete.getURI(), HttpStatus.SC_OK) + ); + } finally { + delete.releaseConnection(); + } + } } diff --git a/src/main/java/com/amihaiemil/docker/RtVolumes.java b/src/main/java/com/amihaiemil/docker/RtVolumes.java index 435dc1e0..1a9f9c02 100644 --- a/src/main/java/com/amihaiemil/docker/RtVolumes.java +++ b/src/main/java/com/amihaiemil/docker/RtVolumes.java @@ -27,9 +27,13 @@ import java.io.IOException; import java.net.URI; +import javax.json.Json; +import javax.json.JsonObjectBuilder; import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; /** * Runtime {@link Volumes}. @@ -80,6 +84,33 @@ public void prune() throws IOException, UnexpectedResponseException { } } + @Override + public void create(final String name) + throws IOException, UnexpectedResponseException { + JsonObjectBuilder json = Json.createObjectBuilder(); + json.add("Name", name); + final HttpPost create = + new HttpPost( + String.format("%s/%s", this.baseUri.toString(), "create") + ); + try { + create.setEntity( + new StringEntity( + json.build().toString(), ContentType.APPLICATION_JSON + ) + ); + this.client.execute( + create, + new MatchStatus( + create.getURI(), + HttpStatus.SC_CREATED + ) + ); + } finally { + create.releaseConnection(); + } + } + @Override public Docker docker() { return this.docker; diff --git a/src/main/java/com/amihaiemil/docker/Volume.java b/src/main/java/com/amihaiemil/docker/Volume.java index 26f23ef0..a4b60567 100644 --- a/src/main/java/com/amihaiemil/docker/Volume.java +++ b/src/main/java/com/amihaiemil/docker/Volume.java @@ -25,8 +25,8 @@ */ package com.amihaiemil.docker; -import javax.json.JsonObject; import java.io.IOException; +import javax.json.JsonObject; /** * A volume manager. @@ -35,7 +35,6 @@ * @version $Id$ * @see Docker Volume API * @since 0.0.6 - * @todo #179:30min Continue implementing create and remove volume operations. * */ public interface Volume extends JsonObject { @@ -50,4 +49,14 @@ public interface Volume extends JsonObject { */ JsonObject inspect() throws IOException, UnexpectedResponseException; + /** + * Remove a volume. + * @param force If true delete volumes that are in use + * @throws IOException If something goes wrong. + * @throws UnexpectedResponseException If the status response is not + * the expected one (200 OK). + * @see Remove a volume + */ + void delete(Boolean force) throws IOException, UnexpectedResponseException; + } diff --git a/src/main/java/com/amihaiemil/docker/Volumes.java b/src/main/java/com/amihaiemil/docker/Volumes.java index f47a58f8..65a0398a 100644 --- a/src/main/java/com/amihaiemil/docker/Volumes.java +++ b/src/main/java/com/amihaiemil/docker/Volumes.java @@ -33,9 +33,23 @@ * @author Boris Kuzmic (boris.kuzmic@gmail.com) * @version $Id$ * @since 0.0.1 + * @todo #183:30min Extend create with options to specify driver, + * driver options and add labels as specified at: + * https://docs.docker.com/engine/api/v1.35/#operation/VolumeCreate */ public interface Volumes extends Iterable { + /** + * Create a volume. + * @param name Name of the volume. + * @throws IOException If something goes wrong. + * @throws UnexpectedResponseException If the status response is not + * the expected one (200 OK). + * @see Create a volume + */ + void create(final String name) + throws IOException, UnexpectedResponseException; + /** * Deletes unused volumes. * @throws IOException If an I/O error occurs. diff --git a/src/test/java/com/amihaiemil/docker/ListedVolumesTestCase.java b/src/test/java/com/amihaiemil/docker/ListedVolumesTestCase.java index d03e97fb..dbe1c0b6 100644 --- a/src/test/java/com/amihaiemil/docker/ListedVolumesTestCase.java +++ b/src/test/java/com/amihaiemil/docker/ListedVolumesTestCase.java @@ -63,6 +63,5 @@ public void iterateAll() { itr.next().getString("Name"), new IsEqual<>("cde2") ); - } } diff --git a/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java b/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java index d23c0c2b..206c99ef 100644 --- a/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java @@ -3,6 +3,9 @@ import com.amihaiemil.docker.mock.AssertRequest; import com.amihaiemil.docker.mock.Condition; import com.amihaiemil.docker.mock.Response; +import java.net.URI; +import javax.json.Json; +import javax.json.JsonObject; import org.apache.http.HttpStatus; import org.hamcrest.MatcherAssert; import org.hamcrest.collection.IsCollectionWithSize; @@ -10,10 +13,6 @@ import org.junit.Test; import org.mockito.Mockito; -import javax.json.Json; -import javax.json.JsonObject; -import java.net.URI; - /** * Unit tests for RtVolume. * @author Boris Kuzmic (boris.kuzmic@gmail.com) @@ -87,4 +86,90 @@ public void inspectsItself() throws Exception { ); } + /** + * RtVolume.delete(false) must send a DELETE request to the volume's url. + * @throws Exception If something goes wrong. + */ + @Test + public void deleteSendsCorrectRequest() throws Exception { + new RtVolume( + Json.createObjectBuilder().build(), + new AssertRequest( + new Response(HttpStatus.SC_OK), + new Condition( + "RtVolume.delete() must send a DELETE HTTP request", + req -> "DELETE".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "RtVolume.delete() must send the request to the volume url", + req -> "http://localhost/volumes/test?force=false".equals( + req.getRequestLine().getUri() + ) + ) + ), + URI.create("http://localhost/volumes/test"), + DOCKER + ).delete(Boolean.FALSE); + } + + /** + * RtVolume.delete(true) must send a DELETE request to the volume's url + * with force param set to true. + * @throws Exception If something goes wrong. + */ + @Test + public void deleteWithForce() throws Exception { + new RtVolume( + Json.createObjectBuilder().build(), + new AssertRequest( + new Response(HttpStatus.SC_OK), + new Condition( + "RtVolume.delete() must send a DELETE HTTP request", + req -> "DELETE".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "RtVolume.delete() must send the request to the volume url", + req -> "http://localhost/volumes/test?force=true".equals( + req.getRequestLine().getUri() + ) + ) + ), + URI.create("http://localhost/volumes/test"), + DOCKER + ).delete(Boolean.TRUE); + } + + /** + * RtVolume.delete(false) must throw UnexpectedResponseException if service + * responds with 404. + * @throws Exception The UnexpectedResponseException + */ + @Test(expected = UnexpectedResponseException.class) + public void deleteErrorOn404() throws Exception { + new RtVolume( + Json.createObjectBuilder().build(), + new AssertRequest( + new Response(HttpStatus.SC_NOT_FOUND) + ), + URI.create("http://localhost/volumes/test"), + DOCKER + ).delete(Boolean.FALSE); + } + + /** + * RtVolume.delete(false) must throw UnexpectedResponseException if service + * responds with 409. + * @throws Exception The UnexpectedResponseException + */ + @Test(expected = UnexpectedResponseException.class) + public void deleteErrorOn409() throws Exception { + new RtVolume( + Json.createObjectBuilder().build(), + new AssertRequest( + new Response(HttpStatus.SC_CONFLICT) + ), + URI.create("http://localhost/volumes/test"), + DOCKER + ).delete(Boolean.FALSE); + } } diff --git a/src/test/java/com/amihaiemil/docker/RtVolumesTestCase.java b/src/test/java/com/amihaiemil/docker/RtVolumesTestCase.java index 545f483c..840c73b3 100644 --- a/src/test/java/com/amihaiemil/docker/RtVolumesTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RtVolumesTestCase.java @@ -66,6 +66,48 @@ public void pruneThrowsErrorOnResponse500() throws Exception { ).prune(); } + /** + * RtVolumes.create() must send a correct POST request sends + * and exist successfully on response code 201. + * @throws Exception If something goes wrong. + */ + @Test + public void createOk() throws Exception { + new ListedVolumes( + new AssertRequest( + new Response(HttpStatus.SC_CREATED), + new Condition( + "RtVolume.create() must send a POST HTTP request", + req -> "POST".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "RtVolume.create() must send the request to the create url", + req -> "http://localhost/volumes/create".equals( + req.getRequestLine().getUri() + ) + ) + ), + URI.create("http://localhost/volumes"), + DOCKER + ).create("test"); + } + + /** + * RtVolumes.create() must throw UnexpectedResponseException if service + * responds with 500. + * @throws Exception The UnexpectedResponseException. + */ + @Test(expected = UnexpectedResponseException.class) + public void createThrowsErrorOnResponse500() throws Exception { + new ListedVolumes( + new AssertRequest( + new Response(HttpStatus.SC_INTERNAL_SERVER_ERROR) + ), + URI.create("http://localhost/volumes"), + DOCKER + ).create("test"); + } + /** * RtVolumes can return its Docker parent. */ From 6aa1d125247e124baf5074104a38c22c7745ad34 Mon Sep 17 00:00:00 2001 From: Boris Kuzmic Date: Mon, 10 Dec 2018 20:25:50 +0100 Subject: [PATCH 2/3] Fixing NPE: when force param is null --- .../java/com/amihaiemil/docker/RtVolume.java | 4 ++- .../amihaiemil/docker/RtVolumeTestCase.java | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/amihaiemil/docker/RtVolume.java b/src/main/java/com/amihaiemil/docker/RtVolume.java index a5afc409..96ae9be7 100644 --- a/src/main/java/com/amihaiemil/docker/RtVolume.java +++ b/src/main/java/com/amihaiemil/docker/RtVolume.java @@ -59,7 +59,9 @@ public void delete(final Boolean force) final UncheckedUriBuilder uri = new UncheckedUriBuilder( this.baseUri.toString() ); - uri.addParameter("force", force.toString()); + if (force != null) { + uri.addParameter("force", force.toString()); + } final HttpDelete delete = new HttpDelete( uri.build() ); diff --git a/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java b/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java index 206c99ef..8f8d1e24 100644 --- a/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java @@ -139,6 +139,32 @@ public void deleteWithForce() throws Exception { ).delete(Boolean.TRUE); } + /** + * RtVolume.delete(null) must send a DELETE request to the volume's url. + * @throws Exception If something goes wrong. + */ + @Test + public void deleteWithNullParameter() throws Exception { + new RtVolume( + Json.createObjectBuilder().build(), + new AssertRequest( + new Response(HttpStatus.SC_OK), + new Condition( + "RtVolume.delete() must send a DELETE HTTP request", + req -> "DELETE".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "RtVolume.delete() must send the request to the volume url", + req -> "http://localhost/volumes/test".equals( + req.getRequestLine().getUri() + ) + ) + ), + URI.create("http://localhost/volumes/test"), + DOCKER + ).delete(null); + } + /** * RtVolume.delete(false) must throw UnexpectedResponseException if service * responds with 404. From fdd74e26d811f29782c1e9c223896370e734cf39 Mon Sep 17 00:00:00 2001 From: Boris Kuzmic Date: Tue, 11 Dec 2018 09:56:00 +0100 Subject: [PATCH 3/3] Renaming delete to remove. Making force optional parameter --- .../java/com/amihaiemil/docker/RtVolume.java | 11 ++-- .../java/com/amihaiemil/docker/Volume.java | 14 ++++- .../amihaiemil/docker/RtVolumeTestCase.java | 58 +++++-------------- 3 files changed, 35 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/amihaiemil/docker/RtVolume.java b/src/main/java/com/amihaiemil/docker/RtVolume.java index 96ae9be7..7efec933 100644 --- a/src/main/java/com/amihaiemil/docker/RtVolume.java +++ b/src/main/java/com/amihaiemil/docker/RtVolume.java @@ -54,14 +54,17 @@ public JsonObject inspect() } @Override - public void delete(final Boolean force) + public void remove() throws IOException, UnexpectedResponseException { + this.remove(false); + } + + @Override + public void remove(final boolean force) throws IOException, UnexpectedResponseException { final UncheckedUriBuilder uri = new UncheckedUriBuilder( this.baseUri.toString() ); - if (force != null) { - uri.addParameter("force", force.toString()); - } + uri.addParameter("force", String.valueOf(force)); final HttpDelete delete = new HttpDelete( uri.build() ); diff --git a/src/main/java/com/amihaiemil/docker/Volume.java b/src/main/java/com/amihaiemil/docker/Volume.java index a4b60567..cb87d7be 100644 --- a/src/main/java/com/amihaiemil/docker/Volume.java +++ b/src/main/java/com/amihaiemil/docker/Volume.java @@ -51,12 +51,22 @@ public interface Volume extends JsonObject { /** * Remove a volume. - * @param force If true delete volumes that are in use * @throws IOException If something goes wrong. * @throws UnexpectedResponseException If the status response is not * the expected one (200 OK). * @see Remove a volume */ - void delete(Boolean force) throws IOException, UnexpectedResponseException; + void remove() throws IOException, UnexpectedResponseException; + + /** + * Remove a volume. + * @param force If true remove volumes that are in use + * @throws IOException If something goes wrong. + * @throws UnexpectedResponseException If the status response is not + * the expected one (200 OK). + * @see Remove a volume + */ + void remove(final boolean force) + throws IOException, UnexpectedResponseException; } diff --git a/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java b/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java index 8f8d1e24..6cc49d63 100644 --- a/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RtVolumeTestCase.java @@ -87,21 +87,21 @@ public void inspectsItself() throws Exception { } /** - * RtVolume.delete(false) must send a DELETE request to the volume's url. + * RtVolume.remove() must send a DELETE request to the volume's url. * @throws Exception If something goes wrong. */ @Test - public void deleteSendsCorrectRequest() throws Exception { + public void removeSendsCorrectRequest() throws Exception { new RtVolume( Json.createObjectBuilder().build(), new AssertRequest( new Response(HttpStatus.SC_OK), new Condition( - "RtVolume.delete() must send a DELETE HTTP request", + "RtVolume.remove() must send a DELETE HTTP request", req -> "DELETE".equals(req.getRequestLine().getMethod()) ), new Condition( - "RtVolume.delete() must send the request to the volume url", + "RtVolume.remove() must send the request to the volume url", req -> "http://localhost/volumes/test?force=false".equals( req.getRequestLine().getUri() ) @@ -109,26 +109,26 @@ public void deleteSendsCorrectRequest() throws Exception { ), URI.create("http://localhost/volumes/test"), DOCKER - ).delete(Boolean.FALSE); + ).remove(); } /** - * RtVolume.delete(true) must send a DELETE request to the volume's url + * RtVolume.remove(true) must send a DELETE request to the volume's url * with force param set to true. * @throws Exception If something goes wrong. */ @Test - public void deleteWithForce() throws Exception { + public void removeWithForce() throws Exception { new RtVolume( Json.createObjectBuilder().build(), new AssertRequest( new Response(HttpStatus.SC_OK), new Condition( - "RtVolume.delete() must send a DELETE HTTP request", + "RtVolume.remove() must send a DELETE HTTP request", req -> "DELETE".equals(req.getRequestLine().getMethod()) ), new Condition( - "RtVolume.delete() must send the request to the volume url", + "RtVolume.remove() must send the request to the volume url", req -> "http://localhost/volumes/test?force=true".equals( req.getRequestLine().getUri() ) @@ -136,42 +136,16 @@ public void deleteWithForce() throws Exception { ), URI.create("http://localhost/volumes/test"), DOCKER - ).delete(Boolean.TRUE); + ).remove(true); } /** - * RtVolume.delete(null) must send a DELETE request to the volume's url. - * @throws Exception If something goes wrong. - */ - @Test - public void deleteWithNullParameter() throws Exception { - new RtVolume( - Json.createObjectBuilder().build(), - new AssertRequest( - new Response(HttpStatus.SC_OK), - new Condition( - "RtVolume.delete() must send a DELETE HTTP request", - req -> "DELETE".equals(req.getRequestLine().getMethod()) - ), - new Condition( - "RtVolume.delete() must send the request to the volume url", - req -> "http://localhost/volumes/test".equals( - req.getRequestLine().getUri() - ) - ) - ), - URI.create("http://localhost/volumes/test"), - DOCKER - ).delete(null); - } - - /** - * RtVolume.delete(false) must throw UnexpectedResponseException if service + * RtVolume.remove() must throw UnexpectedResponseException if service * responds with 404. * @throws Exception The UnexpectedResponseException */ @Test(expected = UnexpectedResponseException.class) - public void deleteErrorOn404() throws Exception { + public void removeErrorOn404() throws Exception { new RtVolume( Json.createObjectBuilder().build(), new AssertRequest( @@ -179,16 +153,16 @@ public void deleteErrorOn404() throws Exception { ), URI.create("http://localhost/volumes/test"), DOCKER - ).delete(Boolean.FALSE); + ).remove(); } /** - * RtVolume.delete(false) must throw UnexpectedResponseException if service + * RtVolume.remove() must throw UnexpectedResponseException if service * responds with 409. * @throws Exception The UnexpectedResponseException */ @Test(expected = UnexpectedResponseException.class) - public void deleteErrorOn409() throws Exception { + public void removeErrorOn409() throws Exception { new RtVolume( Json.createObjectBuilder().build(), new AssertRequest( @@ -196,6 +170,6 @@ public void deleteErrorOn409() throws Exception { ), URI.create("http://localhost/volumes/test"), DOCKER - ).delete(Boolean.FALSE); + ).remove(); } }