diff --git a/src/main/java/com/amihaiemil/docker/RtSwarm.java b/src/main/java/com/amihaiemil/docker/RtSwarm.java index a87ee306..6490a056 100644 --- a/src/main/java/com/amihaiemil/docker/RtSwarm.java +++ b/src/main/java/com/amihaiemil/docker/RtSwarm.java @@ -27,8 +27,15 @@ import java.io.IOException; import java.net.URI; +import javax.json.Json; import javax.json.JsonObject; +import org.apache.http.HttpResponse; +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; +import org.apache.http.util.EntityUtils; /** * Swarm API. @@ -61,4 +68,36 @@ final class RtSwarm implements Swarm { public JsonObject inspect() throws IOException { return new Inspection(this.client, this.baseUri.toString()); } + + @Override + public String init(final String listenAddress) throws IOException { + return this.init( + Json.createObjectBuilder() + .add("ListenAddr", listenAddress) + .add("ForceNewCluster", false) + .build() + ); + } + + @Override + public String init(final JsonObject spec) throws IOException { + final HttpPost init = new HttpPost(this.baseUri.toString() + "/init"); + try { + init.setEntity( + new StringEntity( + spec.toString(), ContentType.APPLICATION_JSON + ) + ); + final HttpResponse response = this.client.execute(init); + final int status = response.getStatusLine().getStatusCode(); + if (status == HttpStatus.SC_OK) { + return EntityUtils.toString(response.getEntity()); + } + throw new UnexpectedResponseException( + init.getRequestLine().getUri(), status, HttpStatus.SC_OK + ); + } finally { + init.releaseConnection(); + } + } } diff --git a/src/main/java/com/amihaiemil/docker/Swarm.java b/src/main/java/com/amihaiemil/docker/Swarm.java index 0b776e8a..e014dde3 100644 --- a/src/main/java/com/amihaiemil/docker/Swarm.java +++ b/src/main/java/com/amihaiemil/docker/Swarm.java @@ -37,7 +37,7 @@ * @author George Aristy (george.aristy@gmail.com) * @version $Id$ * @since 0.0.1 - * @todo #3:30min Implement all of the Swarm operations. See + * @todo #70:30min Continue implementing all of the Swarm operations. See * https://docs.docker.com/engine/api/v1.35/#tag/Swarm for reference and also * the roadmap laid out here: * https://github.com/amihaiemil/docker-java-api/issues/3#issuecomment-375821822 @@ -52,4 +52,22 @@ public interface Swarm { * @throws IOException If something goes wrong. */ JsonObject inspect() throws IOException; + + /** + * Initialize a new swarm. + * @param listenAddress Listen address used for inter-manager communication. + * This can either be in the form of '192.168.1.1:4567' or 'eth0:4567' + * where the port number is optional in both cases. + * @return The swarm's token. + * @throws IOException If something goes wrong. + */ + String init(String listenAddress) throws IOException; + + /** + * Initialize a new swarm by providing a full specification. + * @param spec Full specification for the swarm. + * @return The swarm's token. + * @throws IOException If something goes wrong. + */ + String init(JsonObject spec) throws IOException; } diff --git a/src/test/java/com/amihaiemil/docker/RtSwarmTestCase.java b/src/test/java/com/amihaiemil/docker/RtSwarmTestCase.java index 7f1ce411..27c264c5 100644 --- a/src/test/java/com/amihaiemil/docker/RtSwarmTestCase.java +++ b/src/test/java/com/amihaiemil/docker/RtSwarmTestCase.java @@ -27,6 +27,7 @@ import com.amihaiemil.docker.mock.AssertRequest; import com.amihaiemil.docker.mock.Condition; +import com.amihaiemil.docker.mock.PayloadOf; import com.amihaiemil.docker.mock.Response; import java.net.URI; import org.apache.http.HttpStatus; @@ -131,4 +132,76 @@ public void inspectThrowsErrorIfResponseIs503() throws Exception { ), URI.create("http://localhost") ).inspect(); } + + /** + * The init request should have correct URI, method, and also its JSON + * payload should have at least the 'ListenAddr' and 'ForceNewCluster' + * properties. + * @throws Exception If an error occurs. + */ + @Test + public void initRequestWellformed() throws Exception { + final String listenAddress = "172.27.9.10"; + new RtSwarm( + new AssertRequest( + new Response(HttpStatus.SC_OK, "sometoken123"), + new Condition( + "Request method must be POST.", + req -> "POST".equals(req.getRequestLine().getMethod()) + ), + new Condition( + "The 'ListenAddr' attribute is mandatory.", + req -> new PayloadOf(req).containsKey("ListenAddr") + ), + new Condition( + "The 'ListenAddr' value must be the same as the one given.", + req -> listenAddress.equals( + new PayloadOf(req).getString("ListenAddr") + ) + ), + new Condition( + "The 'ForceNewCluster' attribute is mandatory.", + req -> new PayloadOf(req).containsKey("ForceNewCluster") + ), + new Condition( + "The 'ForceNewCluster' attribute cannot be empty.", + req -> !new PayloadOf(req).isNull("ForceNewCluster") + + ) + ), + URI.create("http://docker/swarm") + ).init(listenAddress); + } + + /** + * Must return the same token returned by Docker. + * @throws Exception If something goes wrong. + */ + @Test + public void initReturnsSwarmToken() throws Exception { + MatcherAssert.assertThat( + new RtSwarm( + new AssertRequest( + new Response(HttpStatus.SC_OK, "sometoken123") + ), + URI.create("http://docker") + ).init("123"), + Matchers.is("sometoken123") + ); + } + + /** + * Must throw {@link UnexpectedResponseException} if docker responds with + * a code different from 200. + * @throws Exception If an error occurs. + */ + @Test(expected = UnexpectedResponseException.class) + public void initUnexpectedErrorIfResponseIsNot200() throws Exception { + new RtSwarm( + new AssertRequest( + new Response(HttpStatus.SC_BAD_REQUEST, "") + ), + URI.create("http://docker") + ).init("123"); + } }