package com.kpelykh.docker.client; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.base.Preconditions; import com.kpelykh.docker.client.model.*; import com.kpelykh.docker.client.utils.CompressArchiveUtil; import com.kpelykh.docker.client.utils.JsonClientFilter; import com.sun.jersey.api.client.*; import com.sun.jersey.api.client.WebResource.Builder; import com.sun.jersey.api.client.config.ClientConfig; import com.sun.jersey.api.client.config.DefaultClientConfig; import com.sun.jersey.api.client.filter.LoggingFilter; import com.sun.jersey.client.apache4.ApacheHttpClient4; import com.sun.jersey.client.apache4.ApacheHttpClient4Handler; import com.sun.jersey.core.util.MultivaluedMapImpl; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.LineIterator; import org.apache.commons.lang.StringUtils; import org.apache.http.client.HttpClient; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.PoolingClientConnectionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import static org.apache.commons.io.IOUtils.closeQuietly; /** * @author Konstantin Pelykh ([email protected]) */ public class DockerClient { private static final Logger LOGGER = LoggerFactory.getLogger(DockerClient.class); private Client client; private String restEndpointUrl; private AuthConfig authConfig; public DockerClient() throws DockerException { this(Config.createConfig()); } public DockerClient(String serverUrl) throws DockerException { this(configWithServerUrl(serverUrl)); } private static Config configWithServerUrl(String serverUrl) throws DockerException { final Config c = Config.createConfig(); c.url = URI.create(serverUrl); return c; } private DockerClient(Config config) { restEndpointUrl = config.url + "/v" + config.version; ClientConfig clientConfig = new DefaultClientConfig(); //clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", config.url.getPort(), PlainSocketFactory.getSocketFactory())); schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory())); PoolingClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry); // Increase max total connection cm.setMaxTotal(1000); // Increase default max connection per route cm.setDefaultMaxPerRoute(1000); HttpClient httpClient = new DefaultHttpClient(cm); client = new ApacheHttpClient4(new ApacheHttpClient4Handler(httpClient, null, false), clientConfig); client.setReadTimeout(10000); //Experimental support for unix sockets: //client = new UnixSocketClient(clientConfig); client.addFilter(new JsonClientFilter()); client.addFilter(new LoggingFilter()); } public void setCredentials(String username, String password, String email) { if (username == null) { throw new IllegalArgumentException("username is null"); } if (password == null) { throw new IllegalArgumentException("password is null"); } if (email == null) { throw new IllegalArgumentException("email is null"); } authConfig = new AuthConfig(); authConfig.setUsername(username); authConfig.setPassword(password); authConfig.setEmail(email); } /** * Authenticate with the server, useful for checking authentication. */ public void auth() throws DockerException { try { client.resource(restEndpointUrl + "/auth") .header("Content-Type", MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .post(authConfig()); } catch (UniformInterfaceException e) { throw new DockerException(e); } } private String registryAuth() throws DockerException { try { return Base64.encodeBase64String(new ObjectMapper().writeValueAsString(authConfig()).getBytes()); } catch (IOException e) { throw new DockerException(e); } } public AuthConfig authConfig() throws DockerException { return authConfig != null ? authConfig : authConfigFromProperties(); } private static AuthConfig authConfigFromProperties() throws DockerException { final AuthConfig a = new AuthConfig(); a.setUsername(Config.createConfig().username); a.setPassword(Config.createConfig().password); a.setEmail(Config.createConfig().email); if (a.getUsername() == null) {throw new IllegalStateException("username is null");} if (a.getPassword() == null) {throw new IllegalStateException("password is null");} if (a.getEmail() == null) {throw new IllegalStateException("email is null");} return a; } /** * * MISC API * * */ public Info info() throws DockerException { WebResource webResource = client.resource(restEndpointUrl + "/info"); try { LOGGER.trace("GET: {}", webResource); return webResource.accept(MediaType.APPLICATION_JSON).get(Info.class); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error.", exception); } else { throw new DockerException(exception); } } } public Version version() throws DockerException { WebResource webResource = client.resource(restEndpointUrl + "/version"); try { LOGGER.trace("GET: {}", webResource); return webResource.accept(MediaType.APPLICATION_JSON).get(Version.class); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error.", exception); } else { throw new DockerException(exception); } } } public int ping() throws DockerException { WebResource webResource = client.resource(restEndpointUrl + "/_ping"); try { LOGGER.trace("GET: {}", webResource); ClientResponse resp = webResource.get(ClientResponse.class); return resp.getStatus(); } catch (UniformInterfaceException exception) { throw new DockerException(exception); } } /** * * IMAGE API * * */ public ClientResponse pull(String repository) throws DockerException { return this.pull(repository, null, null); } public ClientResponse pull(String repository, String tag) throws DockerException { return this.pull(repository, tag, null); } public ClientResponse pull(String repository, String tag, String registry) throws DockerException { Preconditions.checkNotNull(repository, "Repository was not specified"); if (StringUtils.countMatches(repository, ":") == 1) { String repositoryTag[] = StringUtils.split(repository, ':'); repository = repositoryTag[0]; tag = repositoryTag[1]; } MultivaluedMap<String, String> params = new MultivaluedMapImpl(); params.add("tag", tag); params.add("fromImage", repository); params.add("registry", registry); WebResource webResource = client.resource(restEndpointUrl + "/images/create").queryParams(params); try { LOGGER.trace("POST: {}", webResource); return webResource.accept(MediaType.APPLICATION_OCTET_STREAM_TYPE).post(ClientResponse.class); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error.", exception); } else { throw new DockerException(exception); } } } /** * @return The output slurped into a string. */ public static String asString(ClientResponse response) throws IOException { StringWriter out = new StringWriter(); try { LineIterator itr = IOUtils.lineIterator( response.getEntityInputStream(), "UTF-8"); while (itr.hasNext()) { String line = itr.next(); out.write(line + (itr.hasNext() ? "\n" : "")); } } finally { closeQuietly(response.getEntityInputStream()); } return out.toString(); } /** * Push the latest image to the repository. * * @param name The name, e.g. "alexec/busybox" or just "busybox" if you want to default. Not null. */ public ClientResponse push(final String name) throws DockerException { if (name == null) { throw new IllegalArgumentException("name is null"); } try { final String registryAuth = registryAuth(); return client.resource(restEndpointUrl + "/images/" + name(name) + "/push") .header("X-Registry-Auth", registryAuth) .accept(MediaType.APPLICATION_JSON) .post(ClientResponse.class); } catch (UniformInterfaceException e) { throw new DockerException(e); } } private String name(String name) { return name.contains("/") ? name : authConfig.getUsername(); } /** * Tag an image into a repository * * @param image the local image to tag (either a name or an id) * @param repository the repository to tag in * @param tag any tag for this image * @param force (not documented) * @return the HTTP status code (201 for success) */ public int tag(String image, String repository, String tag, boolean force) throws DockerException { Preconditions.checkNotNull(image, "image was not specified"); Preconditions.checkNotNull(repository, "repository was not specified"); Preconditions.checkNotNull(tag, " tag was not provided"); MultivaluedMap<String, String> params = new MultivaluedMapImpl(); params.add("repo", repository); params.add("tag", tag); params.add("force", String.valueOf(force)); WebResource webResource = client.resource(restEndpointUrl + "/images/" + image + "/tag").queryParams(params); try { LOGGER.trace("POST: {}", webResource); ClientResponse resp = webResource.post(ClientResponse.class); return resp.getStatus(); } catch (UniformInterfaceException exception) { throw new DockerException(exception); } } /** * Create an image by importing the given stream of a tar file. * * @param repository the repository to import to * @param tag any tag for this image * @param imageStream the InputStream of the tar file * @return an {@link ImageCreateResponse} containing the id of the imported image * @throws DockerException if the import fails for some reason. */ public ImageCreateResponse importImage(String repository, String tag, InputStream imageStream) throws DockerException { Preconditions.checkNotNull(repository, "Repository was not specified"); Preconditions.checkNotNull(imageStream, "imageStream was not provided"); MultivaluedMap<String, String> params = new MultivaluedMapImpl(); params.add("repo", repository); params.add("tag", tag); params.add("fromSrc", "-"); WebResource webResource = client.resource(restEndpointUrl + "/images/create").queryParams(params); try { LOGGER.trace("POST: {}", webResource); return webResource.accept(MediaType.APPLICATION_OCTET_STREAM_TYPE).post(ImageCreateResponse.class, imageStream); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error.", exception); } else { throw new DockerException(exception); } } } public List<SearchItem> search(String search) throws DockerException { WebResource webResource = client.resource(restEndpointUrl + "/images/search").queryParam("term", search); try { return webResource.accept(MediaType.APPLICATION_JSON).get(new GenericType<List<SearchItem>>() { }); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error.", exception); } else { throw new DockerException(exception); } } } /** * Remove an image, deleting any tags it might have. */ public void removeImage(String imageId) throws DockerException { Preconditions.checkState(!StringUtils.isEmpty(imageId), "Image ID can't be empty"); try { WebResource webResource = client.resource(restEndpointUrl + "/images/" + imageId) .queryParam("force", "true"); LOGGER.trace("DELETE: {}", webResource); webResource.delete(); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 204) { //no error LOGGER.trace("Successfully removed image " + imageId); } else if (exception.getResponse().getStatus() == 404) { LOGGER.warn("{} no such image", imageId); } else if (exception.getResponse().getStatus() == 409) { throw new DockerException("Conflict"); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error.", exception); } else { throw new DockerException(exception); } } } public void removeImages(List<String> images) throws DockerException { Preconditions.checkNotNull(images, "List of images can't be null"); for (String imageId : images) { removeImage(imageId); } } public String getVizImages() throws DockerException { WebResource webResource = client.resource(restEndpointUrl + "/images/viz"); try { LOGGER.trace("GET: {}", webResource); String response = webResource.get(String.class); LOGGER.trace("Response: {}", response); return response; } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 400) { throw new DockerException("bad parameter"); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public List<Image> getImages() throws DockerException { return this.getImages(null, false); } public List<Image> getImages(boolean allContainers) throws DockerException { return this.getImages(null, allContainers); } public List<Image> getImages(String name) throws DockerException { return this.getImages(name, false); } public List<Image> getImages(String name, boolean allImages) throws DockerException { MultivaluedMap<String, String> params = new MultivaluedMapImpl(); params.add("filter", name); params.add("all", allImages ? "1" : "0"); WebResource webResource = client.resource(restEndpointUrl + "/images/json").queryParams(params); try { LOGGER.trace("GET: {}", webResource); List<Image> images = webResource.accept(MediaType.APPLICATION_JSON).get(new GenericType<List<Image>>() { }); LOGGER.trace("Response: {}", images); return images; } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 400) { throw new DockerException("bad parameter"); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(); } } } public ImageInspectResponse inspectImage(String imageId) throws DockerException, NotFoundException { WebResource webResource = client.resource(restEndpointUrl + String.format("/images/%s/json", imageId)); try { LOGGER.trace("GET: {}", webResource); return webResource.accept(MediaType.APPLICATION_JSON).get(ImageInspectResponse.class); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 404) { throw new NotFoundException(String.format("No such image %s", imageId)); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } /** * * CONTAINER API * * */ public List<Container> listContainers(boolean allContainers) { return this.listContainers(allContainers, false, -1, false, null, null); } public List<Container> listContainers(boolean allContainers, boolean latest) { return this.listContainers(allContainers, latest, -1, false, null, null); } public List<Container> listContainers(boolean allContainers, boolean latest, int limit) { return this.listContainers(allContainers, latest, limit, false, null, null); } public List<Container> listContainers(boolean allContainers, boolean latest, int limit, boolean showSize) { return this.listContainers(allContainers, latest, limit, showSize, null, null); } public List<Container> listContainers(boolean allContainers, boolean latest, int limit, boolean showSize, String since) { return this.listContainers(allContainers, latest, limit, false, since, null); } public List<Container> listContainers(boolean allContainers, boolean latest, int limit, boolean showSize, String since, String before) { MultivaluedMap<String, String> params = new MultivaluedMapImpl(); params.add("limit", latest ? "1" : String.valueOf(limit)); params.add("all", allContainers ? "1" : "0"); params.add("since", since); params.add("before", before); params.add("size", showSize ? "1" : "0"); WebResource webResource = client.resource(restEndpointUrl + "/containers/json").queryParams(params); LOGGER.trace("GET: {}", webResource); List<Container> containers = webResource.accept(MediaType.APPLICATION_JSON).get(new GenericType<List<Container>>() { }); LOGGER.trace("Response: {}", containers); return containers; } public ContainerCreateResponse createContainer(ContainerConfig config) throws DockerException { return createContainer(config, null); } public ContainerCreateResponse createContainer(ContainerConfig config, String name) throws DockerException, NotFoundException { MultivaluedMap<String, String> params = new MultivaluedMapImpl(); if (name != null) { params.add("name", name); } WebResource webResource = client.resource(restEndpointUrl + "/containers/create").queryParams(params); try { LOGGER.trace("POST: {} ", webResource); return webResource.accept(MediaType.APPLICATION_JSON) .type(MediaType.APPLICATION_JSON) .post(ContainerCreateResponse.class, config); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 404) { throw new NotFoundException(String.format("%s is an unrecognized image. Please pull the image first.", config.getImage())); } else if (exception.getResponse().getStatus() == 406) { throw new DockerException("impossible to attach (container not running)"); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public void startContainer(String containerId) throws DockerException { this.startContainer(containerId, null); } public void startContainer(String containerId, HostConfig hostConfig) throws DockerException, NotFoundException { WebResource webResource = client.resource(restEndpointUrl + String.format("/containers/%s/start", containerId)); try { LOGGER.trace("POST: {}", webResource); Builder builder = webResource.accept(MediaType.TEXT_PLAIN); if (hostConfig != null) { builder.type(MediaType.APPLICATION_JSON).post(hostConfig); } else { builder.post((HostConfig) null); } } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 404) { throw new NotFoundException(String.format("No such container %s", containerId)); } else if (exception.getResponse().getStatus() == 204) { //no error LOGGER.trace("Successfully started container {}", containerId); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public ContainerInspectResponse inspectContainer(String containerId) throws DockerException, NotFoundException { WebResource webResource = client.resource(restEndpointUrl + String.format("/containers/%s/json", containerId)); try { LOGGER.trace("GET: {}", webResource); return webResource.accept(MediaType.APPLICATION_JSON).get(ContainerInspectResponse.class); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 404) { throw new NotFoundException(String.format("No such container %s", containerId)); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public void removeContainer(String container) throws DockerException { this.removeContainer(container, false); } public void removeContainer(String containerId, boolean removeVolumes) throws DockerException { Preconditions.checkState(!StringUtils.isEmpty(containerId), "Container ID can't be empty"); WebResource webResource = client.resource(restEndpointUrl + "/containers/" + containerId).queryParam("v", removeVolumes ? "1" : "0"); try { LOGGER.trace("DELETE: {}", webResource); String response = webResource.accept(MediaType.APPLICATION_JSON).delete(String.class); LOGGER.trace("Response: {}", response); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 204) { //no error LOGGER.trace("Successfully removed container " + containerId); } else if (exception.getResponse().getStatus() == 400) { throw new DockerException("bad parameter"); } else if (exception.getResponse().getStatus() == 404) { // should really throw a NotFoundException instead of silently ignoring the problem LOGGER.warn(String.format("%s is an unrecognized container.", containerId)); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public void removeContainers(List<String> containers, boolean removeVolumes) throws DockerException { Preconditions.checkNotNull(containers, "List of containers can't be null"); for (String containerId : containers) { removeContainer(containerId, removeVolumes); } } public int waitContainer(String containerId) throws DockerException, NotFoundException { WebResource webResource = client.resource(restEndpointUrl + String.format("/containers/%s/wait", containerId)); try { LOGGER.trace("POST: {}", webResource); ObjectNode ObjectNode = webResource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).post(ObjectNode.class); return ObjectNode.get("StatusCode").asInt(); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 404) { throw new NotFoundException(String.format("No such container %s", containerId)); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } catch (Exception e) { throw new DockerException(e); } } public ClientResponse logContainer(String containerId) throws DockerException { return logContainer(containerId, false); } public ClientResponse logContainerStream(String containerId) throws DockerException { return logContainer(containerId, true); } private ClientResponse logContainer(String containerId, boolean stream) throws DockerException, NotFoundException { MultivaluedMap<String, String> params = new MultivaluedMapImpl(); params.add("logs", "1"); params.add("stdout", "1"); params.add("stderr", "1"); if (stream) { params.add("stream", "1"); // this parameter keeps stream open indefinitely } WebResource webResource = client.resource(restEndpointUrl + String.format("/containers/%s/attach", containerId)) .queryParams(params); try { LOGGER.trace("POST: {}", webResource); return webResource.accept(MediaType.APPLICATION_OCTET_STREAM_TYPE).post(ClientResponse.class, params); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 400) { throw new DockerException("bad parameter"); } else if (exception.getResponse().getStatus() == 404) { throw new NotFoundException(String.format("No such container %s", containerId)); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public ClientResponse copyFile(String containerId, String resource) throws DockerException { CopyConfig copyConfig = new CopyConfig(); copyConfig.setResource(resource); WebResource webResource = client.resource(restEndpointUrl + String.format("/containers/%s/copy", containerId)); try { LOGGER.trace("POST: " + webResource.toString()); WebResource.Builder builder = webResource.accept(MediaType.APPLICATION_OCTET_STREAM_TYPE).type("application/json"); return builder.post(ClientResponse.class, copyConfig.toString()); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 400) { throw new DockerException("bad parameter"); } else if (exception.getResponse().getStatus() == 404) { throw new DockerException(String.format("No such container %s", containerId)); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public List<ChangeLog> containerDiff(String containerId) throws DockerException, NotFoundException { WebResource webResource = client.resource(restEndpointUrl + String.format("/containers/%s/changes", containerId)); try { LOGGER.trace("GET: {}", webResource); return webResource.accept(MediaType.APPLICATION_JSON).get(new GenericType<List<ChangeLog>>() { }); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 404) { throw new NotFoundException(String.format("No such container %s", containerId)); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public void stopContainer(String containerId) throws DockerException { this.stopContainer(containerId, 10); } public void stopContainer(String containerId, int timeout) throws DockerException { WebResource webResource = client.resource(restEndpointUrl + String.format("/containers/%s/stop", containerId)) .queryParam("t", String.valueOf(timeout)); try { LOGGER.trace("POST: {}", webResource); webResource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).post(); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 404) { LOGGER.warn("No such container {}", containerId); } else if (exception.getResponse().getStatus() == 204) { //no error LOGGER.trace("Successfully stopped container {}", containerId); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public void kill(String containerId) throws DockerException { WebResource webResource = client.resource(restEndpointUrl + String.format("/containers/%s/kill", containerId)); try { LOGGER.trace("POST: {}", webResource); webResource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).post(); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 404) { LOGGER.warn("No such container {}", containerId); } else if (exception.getResponse().getStatus() == 204) { //no error LOGGER.trace("Successfully killed container {}", containerId); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public void restart(String containerId, int timeout) throws DockerException, NotFoundException { WebResource webResource = client.resource(restEndpointUrl + String.format("/containers/%s/restart", containerId)); try { LOGGER.trace("POST: {}", webResource); webResource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).post(); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 404) { throw new NotFoundException(String.format("No such container %s", containerId)); } else if (exception.getResponse().getStatus() == 204) { //no error LOGGER.trace("Successfully restarted container {}", containerId); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } } public String commit(CommitConfig commitConfig) throws DockerException, NotFoundException { Preconditions.checkNotNull(commitConfig.getContainer(), "Container ID was not specified"); MultivaluedMap<String, String> params = new MultivaluedMapImpl(); params.add("container", commitConfig.getContainer()); params.add("repo", commitConfig.getRepo()); params.add("tag", commitConfig.getTag()); params.add("m", commitConfig.getMessage()); params.add("author", commitConfig.getAuthor()); params.add("run", commitConfig.getRun()); WebResource webResource = client.resource(restEndpointUrl + "/commit").queryParams(params); try { LOGGER.trace("POST: {}", webResource); ObjectNode ObjectNode = webResource.accept("application/vnd.docker.raw-stream").post(ObjectNode.class, params); return ObjectNode.get("Id").asText(); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 404) { throw new NotFoundException(String.format("No such container %s", commitConfig.getContainer())); } else if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } catch (Exception e) { throw new DockerException(e); } } public ClientResponse build(File dockerFolder) throws DockerException { return this.build(dockerFolder, null); } public ClientResponse build(File dockerFolder, String tag) throws DockerException { return this.build(dockerFolder, tag, false); } private static boolean isFileResource(String resource) { URI uri; try { uri = new URI(resource); } catch (URISyntaxException e) { throw new RuntimeException(e); } return uri.getScheme() == null || "file".equals(uri.getScheme()); } public ClientResponse build(File dockerFolder, String tag, boolean noCache) throws DockerException { Preconditions.checkNotNull(dockerFolder, "Folder is null"); Preconditions.checkArgument(dockerFolder.exists(), "Folder %s doesn't exist", dockerFolder); Preconditions.checkState(new File(dockerFolder, "Dockerfile").exists(), "Dockerfile doesn't exist in " + dockerFolder); //We need to use Jersey HttpClient here, since ApacheHttpClient4 will not add boundary filed to //Content-Type: multipart/form-data; boundary=Boundary_1_372491238_1372806136625 MultivaluedMap<String, String> params = new MultivaluedMapImpl(); params.add("t", tag); if (noCache) { params.add("nocache", "true"); } // ARCHIVE TAR String archiveNameWithOutExtension = UUID.randomUUID().toString(); File dockerFolderTar = null; try { File dockerFile = new File(dockerFolder, "Dockerfile"); List<String> dockerFileContent = FileUtils.readLines(dockerFile); if (dockerFileContent.size() <= 0) { throw new DockerException(String.format("Dockerfile %s is empty", dockerFile)); } List<File> filesToAdd = new ArrayList<File>(); filesToAdd.add(dockerFile); for (String cmd : dockerFileContent) { if (StringUtils.startsWithIgnoreCase(cmd.trim(), "ADD")) { String addArgs[] = StringUtils.split(cmd, " \t"); if (addArgs.length != 3) { throw new DockerException(String.format("Wrong format on line [%s]", cmd)); } String resource = addArgs[1]; if(isFileResource(resource)) { File src = new File(resource); if (!src.isAbsolute()) { src = new File(dockerFolder, resource).getCanonicalFile(); } else { throw new DockerException(String.format("Source file %s must be relative to %s", src, dockerFolder)); } if (!src.exists()) { throw new DockerException(String.format("Source file %s doesn't exist", src)); } if (src.isDirectory()) { filesToAdd.addAll(FileUtils.listFiles(src, null, true)); } else { filesToAdd.add(src); } } } } dockerFolderTar = CompressArchiveUtil.archiveTARFiles(dockerFolder, filesToAdd, archiveNameWithOutExtension); } catch (IOException ex) { FileUtils.deleteQuietly(dockerFolderTar); throw new DockerException("Error occurred while preparing Docker context folder.", ex); } WebResource webResource = client.resource(restEndpointUrl + "/build").queryParams(params); try { LOGGER.trace("POST: {}", webResource); return webResource .type("application/tar") .accept(MediaType.TEXT_PLAIN) .post(ClientResponse.class, FileUtils.openInputStream(dockerFolderTar)); } catch (UniformInterfaceException exception) { if (exception.getResponse().getStatus() == 500) { throw new DockerException("Server error", exception); } else { throw new DockerException(exception); } } catch (IOException e) { throw new DockerException(e); } finally { FileUtils.deleteQuietly(dockerFolderTar); } } }