Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/main/java/com/amihaiemil/docker/Images.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@
package com.amihaiemil.docker;

import java.io.IOException;
import java.net.URL;

/**
* Images API.
* @author Mihai Andronache (amihaiemil@gmail.com)
* @version $Id$
* @see <a href="https://docs.docker.com/engine/api/v1.35/#tag/Image">Docker Images API</a>
* @since 0.0.1
* @todo #71:30min Continue implementing the rest of the operations for the
* @todo #83:30min Keep implementing the rest of the operations for the
* Images interface. See the docs referenced above for more details.
*/
public interface Images {
Expand All @@ -48,4 +49,20 @@ public interface Images {
* just iterate on 'docker.images()'.
*/
Iterable<Image> iterate() throws IOException, UnexpectedResponseException;

/**
* Creates an image by pulling it from a registry.
* @param name Name of the image to pull.
* @param source The URL from which the image can be retrieved.
* @param repo Repository name for the image when it is pulled.
* @param tag Tag or digest for the image.
* @return This {@link Images}.
* @throws IOException If an I/O error occurs.
* @throws UnexpectedResponseException If the API responds with an
* unexpected status.
* @checkstyle ParameterNumber (4 lines)
*/
Images create(
final String name, final URL source, final String repo, final String tag
) throws IOException, UnexpectedResponseException;
}
35 changes: 35 additions & 0 deletions src/main/java/com/amihaiemil/docker/RtImages.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.stream.Collectors;
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.HttpGet;
import org.apache.http.client.methods.HttpPost;

/**
* Runtime {@link Images}.
Expand Down Expand Up @@ -90,4 +92,37 @@ public Iterable<Image> iterate() throws IOException {
get.releaseConnection();
}
}

// @todo #83:30min Several API calls required an authentication header as
// explained here:
// https://docs.docker.com/engine/api/v1.35/#section/Authentication
// (including Images.create()). Find a way to make a reusable object from
// that action and introduce it here.
// @checkstyle ParameterNumber (4 lines)
@Override
public Images create(
final String name, final URL source, final String repo, final String tag
) throws IOException, UnexpectedResponseException {
final HttpPost create = new HttpPost(
new UncheckedUriBuilder(this.baseUri.toString().concat("/create"))
.addParameter("fromImage", name)
.addParameter("fromSrc", source.toString())
.addParameter("repo", repo)
.addParameter("tag", tag)
.build()
);
try {
final int status = this.client.execute(create)
.getStatusLine()
.getStatusCode();
if (HttpStatus.SC_OK != status) {
throw new UnexpectedResponseException(
create.getURI().toString(), status, HttpStatus.SC_OK
);
}
return this;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llorllale Why return this here?

Maybe we could return Image -- the created image. Since we have its name and, according to the API, we can use the id or name to do operations on an Image. WDYT? If you think it's ok, leave a puzzle for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amihaiemil I'm not sure which sequence of commands for the docker API you're referring to.

I see that search images is exclusive to Docker Hub.

The docs for create image do not show an example response with the image's ID.

The example responses for list images do not include the name.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llorllale ok, leave it like this for now, I'll think more about it

} finally {
create.releaseConnection();
}
}
}
71 changes: 71 additions & 0 deletions src/main/java/com/amihaiemil/docker/UncheckedUriBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* 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.net.URI;
import java.net.URISyntaxException;
import org.apache.http.client.utils.URIBuilder;

/**
* A {@link URIBuilder} that hides checked exceptions in the methods used
* throughout this library. Used under the assumption that the structure
* of URIs created using this class are valid.
* @author George Aristy (george.aristy@gmail.com)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llorllale don't forget @version and @since

Copy link
Contributor Author

@llorllale llorllale May 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amihaiemil ok - the build should have failed, no?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llorllale nope, I think those rules are in qulice, we use only checkstyle

* @version $Id$
* @since 0.0.1
*/
final class UncheckedUriBuilder extends URIBuilder {
/**
* Ctor.
* @param uri Base URI.
* @throws IllegalArgumentException From {@link URI#create(String)}.
* @throws NullPointerException From {@link URI#create(String)}.
*/
UncheckedUriBuilder(
final String uri
) throws IllegalArgumentException, NullPointerException {
super(URI.create(uri));
}

@Override
public UncheckedUriBuilder addParameter(
final String name, final String value
) {
super.addParameter(name, value);
return this;
}

@Override
public URI build() {
try {
return super.build();
} catch (final URISyntaxException ex) {
throw new IllegalStateException(
"Unexpected error while building a URI!", ex
);
}
}
}
64 changes: 64 additions & 0 deletions src/test/java/com/amihaiemil/docker/RtImagesTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
package com.amihaiemil.docker;

import com.amihaiemil.docker.mock.AssertRequest;
import com.amihaiemil.docker.mock.Condition;
import com.amihaiemil.docker.mock.Response;
import java.net.URI;
import java.net.URL;
import java.util.concurrent.atomic.AtomicInteger;
import javax.json.Json;
import org.apache.http.HttpStatus;
Expand Down Expand Up @@ -85,4 +87,66 @@ public void iterateFailsIfResponseIs500() throws Exception {
URI.create("http://localhost")
).iterate();
}

/**
* {@link RtImages#create(String, URL, String, String)} must construct the
* URL with parameters correctly.
* <p>
* Notice the escaped characters for the 'fromSrc' parameter's value.
* @throws Exception If an error occurs.
*/
@Test
public void createSetsGivenParameters() throws Exception {
new RtImages(
new AssertRequest(
new Response(HttpStatus.SC_OK),
new Condition(
// @checkstyle LineLength (1 line)
"RtImages.create() failed to correctly build the request URI.",
req -> {
System.out.println(req.getRequestLine().getUri());
return req.getRequestLine().getUri().endsWith(
// @checkstyle LineLength (1 line)
"/create?fromImage=testImage&fromSrc=http%3A%2F%2Fdocker.registry.com&repo=testRepo&tag=1.23"
);
}
)
),
URI.create("http://localhost")
)
.create(
"testImage", new URL("http://docker.registry.com"),
"testRepo", "1.23"
);
}

/**
* RtImages.create() must throw an {@link UnexpectedResponseException}
* if the docker API responds with status code 404.
* @throws Exception The UnexpectedResponseException.
*/
@Test(expected = UnexpectedResponseException.class)
public void createErrorOnStatus404() throws Exception {
new RtImages(
new AssertRequest(
new Response(HttpStatus.SC_NOT_FOUND)
),
URI.create("http://localhost")
).create("", new URL("http://registry.docker.com"), "", "");
}

/**
* RtImages.create() must throw an {@link UnexpectedResponseException}
* if the docker API responds with status code 500.
* @throws Exception The UnexpectedResponseException.
*/
@Test(expected = UnexpectedResponseException.class)
public void createErrorOnStatus500() throws Exception {
new RtImages(
new AssertRequest(
new Response(HttpStatus.SC_INTERNAL_SERVER_ERROR)
),
URI.create("http://localhost")
).create("", new URL("http://registry.docker.com"), "", "");
}
}
10 changes: 10 additions & 0 deletions src/test/java/com/amihaiemil/docker/mock/Response.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ public final class Response implements HttpResponse {
*/
private final HttpEntity payload;

/**
* Ctor.
* <p>
* Response with no payload.
* @param status The {@link HttpStatus http status code}
*/
public Response(final int status) {
this(status, "");
}

/**
* Ctor.
*
Expand Down