diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..a96e5522 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git/ +target/ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..9a39f917 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Unix shell files use Unix line endings +*.sh text eol=lf + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..985c3569 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: java +jdk: + - oraclejdk7 +after_success: + - echo "ossrh\${env.OSSRH_USER}\${env.OSSRH_PASS}" > ~/settings.xml + - mvn deploy -DskipITs --settings ~/settings.xml diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..ea311817 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,179 @@ +Change Log +=== +Latest SNAPSHOT +--- + +v1.3.0 +--- +* [#213](https://github.com/docker-java/docker-java/pull/213) Add ulimit support +* [#208](https://github.com/docker-java/docker-java/pull/208) Added PullEventStreamItem and EventStreamReader to stream the reading of events +* [#205](https://github.com/docker-java/docker-java/issues/205) Access mode of VolumesRW incorrectly deserialized +* [#204] (https://github.com/docker-java/docker-java/pull/204) Added support to use the credentials from .dockercfg during build +* [#203](https://github.com/docker-java/docker-java/issues/203) Missing 'MacAddress' option in create container command +* [#197](https://github.com/docker-java/docker-java/pull/197) Allow for null bindings + +v1.2.0 +--- +* [#194](https://github.com/docker-java/docker-java/pull/194) Fixed remove intermediate containers bug on build goal +* [#193](https://github.com/docker-java/docker-java/pull/193) Add HostConfig related methods from start command to create command +* [#192](https://github.com/docker-java/docker-java/pull/192) Added a Links constructor accepting a List object + +v1.1.0 +--- + + * [#186](https://github.com/docker-java/docker-java/pull/186) Added withPull method to BuilImageCmd + * [#185](https://github.com/docker-java/docker-java/pull/185) Introduce WrappedResponseInputStream to close underlying Response + * [#180](https://github.com/docker-java/docker-java/pull/180) Dockerfiles not called 'dockerfile' + * [#179](https://github.com/docker-java/docker-java/pull/179) Add support for cpuset in CreateContainerCmd + * [#170](https://github.com/docker-java/docker-java/pull/170) Allow to specify alternative files other than 'Dockerfile' for building images + * [#165](https://github.com/docker-java/docker-java/pull/165) PushImageCmd assumes that you have an auth config setup + * [#161](https://github.com/docker-java/docker-java/pull/161) Inspect exec command + * [#159](https://github.com/docker-java/docker-java/pull/159) Add missing Info fields + * [#156](https://github.com/docker-java/docker-java/pull/156) Add support for configuring ExtraHosts + * [#146](https://github.com/docker-java/docker-java/pull/146) Create Identifier type + + +v1.0.0 +--- + * [#152](https://github.com/docker-java/docker-java/pull/152) Restore guava as a dependency + * [#149](https://github.com/docker-java/docker-java/pull/149) Handle HTTP-Redirects + * [#148](https://github.com/docker-java/docker-java/pull/148) Save image functionality + * [#142](https://github.com/docker-java/docker-java/pull/142) Reduce Logging Level + * [#138](https://github.com/docker-java/docker-java/pull/138) Apache CXF interopabilty + * [#137](https://github.com/docker-java/docker-java/pull/137) Multiple volumesFrom option when creating a container + * [#135](https://github.com/docker-java/docker-java/pull/135) Update to latest unix-socket-factory + * [#134](https://github.com/docker-java/docker-java/pull/134) Remove Google Guava as dependency + * [#131](https://github.com/docker-java/docker-java/pull/128) Utility classes and streamed JSON representations + * [#128](https://github.com/docker-java/docker-java/pull/128) Allow unauthorized pullImageCmd + +v0.10.5 +--- + * [#125](https://github.com/docker-java/docker-java/pull/125) Unixsocket support + * [#123](https://github.com/docker-java/docker-java/pull/123) support DOCKER_TLS_VERIFY to detect ssl + * [#121](https://github.com/docker-java/docker-java/pull/121) Implemented support for commands: Exec-start, Exec-create + * [#120](https://github.com/docker-java/docker-java/pull/120) Command resource cleanup after command execution + * [#118](https://github.com/docker-java/docker-java/pull/118) Use chunked encoding when passing the docker image + * [#116](https://github.com/docker-java/docker-java/pull/116) Allow SSL configuration from pre-existing keystore to be used + * [#115](https://github.com/docker-java/docker-java/pull/115) Polish RestartPolicy + * [#114](https://github.com/docker-java/docker-java/pull/114) Fix CreateContainerCmdImpl.withVolumesFrom() + * [#111](https://github.com/docker-java/docker-java/pull/111) Allow to send empty messages in StartContainerCmd + +v0.10.4 +--- + + * [#109](https://github.com/docker-java/docker-java/pull/109) Support tag in push image command + * [#106](https://github.com/docker-java/docker-java/pull/106) Allow to manage Linux capabilities in CreateContainerCmd + * [#105](https://github.com/docker-java/docker-java/pull/105) Allow to pass HostConfig when creating a container + * [#103](https://github.com/docker-java/docker-java/pull/103) Make GoLangMatchFileFilter work on Windows + * [#102](https://github.com/docker-java/docker-java/pull/102) Downgraded jackson-jaxrs dependency version + * [#101](https://github.com/docker-java/docker-java/pull/101) list filtered images as described in the remote api docs + * [#100](https://github.com/docker-java/docker-java/pull/100) Add support for .dockercfg files to handle auth for push command + * [#95](https://github.com/docker-java/docker-java/pull/95) Add support for .dockerignore files + * [#92](https://github.com/docker-java/docker-java/pull/92) Add travis-ci support + * [#90](https://github.com/docker-java/docker-java/pull/90) Update DockerClientBuilder.java + * [#88](https://github.com/docker-java/docker-java/pull/88) Add support for private repositories and pull/push authentication + +v0.10.3 +--- + + * [#87](https://github.com/docker-java/docker-java/pull/87) Improve adding of port bindings + * [#83](https://github.com/docker-java/docker-java/pull/83) Loading of custom DockerCmdExecFactory + * [#81](https://github.com/docker-java/docker-java/pull/81) Env config + * [#82](https://github.com/docker-java/docker-java/pull/82) Allow multiple port bindings per ExposedPort + * [#80](https://github.com/docker-java/docker-java/pull/80) explicitly use the latest image version + * [#79](https://github.com/docker-java/docker-java/pull/79) Move slow tests to integration test phase, enable tests by default + * [#76](https://github.com/docker-java/docker-java/pull/76) New enum \"InternetProtocol\" for supported IP protocols replaces \"scheme\" + * [#75](https://github.com/docker-java/docker-java/pull/75) Use ExposedPort.toString() in serialization + * [#74](https://github.com/docker-java/docker-java/pull/74) Use project.build.sourceEncoding in compiler + * [#73](https://github.com/docker-java/docker-java/pull/73) Improve parsing and serialization of Link + * [#70](https://github.com/docker-java/docker-java/pull/70) Improve instantiation and serialization of Bind + +v0.10.2 +--- + + * [#69](https://github.com/docker-java/docker-java/pull/69) Use canonical form of Docker folder when building TAR files + * [#68](https://github.com/docker-java/docker-java/pull/68) Set Jersey client CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE + * [#67](https://github.com/docker-java/docker-java/pull/67) typo in README.md + * [#65](https://github.com/docker-java/docker-java/pull/65) Added static method udp in ExposedPort + * [#63](https://github.com/docker-java/docker-java/pull/63) Bind.parse() fails when access mode is specified + * [#57](https://github.com/docker-java/docker-java/pull/57) Add streaming events API + * [#59](https://github.com/docker-java/docker-java/pull/59) Update readme to include corrected api example + * [#2](https://github.com/docker-java/docker-java/pull/2) Move to new maven coordinate com.github.docker-java:docker-java + * [#56](https://github.com/docker-java/docker-java/pull/56) Update README.md + * [#58](https://github.com/docker-java/docker-java/pull/58) Code clear and bug fix + +v0.10.1 +--- + + * [#49](https://github.com/docker-java/docker-java/pull/49) Allow user to check where volume is binded on host + * [#47](https://github.com/docker-java/docker-java/pull/47) let CompressArchiveUtil preserve executable flags + * [#46](https://github.com/docker-java/docker-java/pull/46) Fixes to AttachContainerCmd and CreateContainerCmd. + +v0.10.0 +--- + + * [#45](https://github.com/docker-java/docker-java/pull/45) Fix Issue #44 Adjusting DNS property type to be a String array as specified by the Doc... + * [#38](https://github.com/docker-java/docker-java/pull/38) Remove special-case for one \":\" from PullCommand + * [#37](https://github.com/docker-java/docker-java/pull/37) Starts v0.10.0 + * [#35](https://github.com/docker-java/docker-java/pull/35) Exposing the withTTY method for container creation. + +v0.9.1 +--- + + * [#31](https://github.com/docker-java/docker-java/pull/31) Change VolumesFrom to handle array + * [#29](https://github.com/docker-java/docker-java/pull/29) Makes Config a public, immutable class with a builder + * [#22](https://github.com/docker-java/docker-java/pull/22) Fixes for startContainerCmd + * [#19](https://github.com/docker-java/docker-java/pull/19) Add missing options to BuildCmd and set them to match Docker client. + * [#16](https://github.com/docker-java/docker-java/pull/16) Build image improvements + +v0.9.0 +--- + + * [#14](https://github.com/docker-java/docker-java/pull/14) Use RegEx to match ADD command from Dockerfile. + * [#9](https://github.com/docker-java/docker-java/pull/9) Add a build command accepting a tar as a InputStream, so we can build the Dockerfile TAR on the fly without a temporary folder. + +v0.8.2 +--- + + * [#2](https://github.com/docker-java/docker-java/pull/2) Move to new maven coordinate com.github.docker-java:docker-java + * [#1](https://github.com/docker-java/docker-java/pull/1) Merge + * [#2](https://github.com/docker-java/docker-java/pull/2) Move to new maven coordinate com.github.docker-java:docker-java + * [#66](https://github.com/docker-java/docker-java/pull/66) Backport the new structure to Jersey 1.18 + * [#65](https://github.com/docker-java/docker-java/pull/65) Added static method udp in ExposedPort + * [#61](https://github.com/docker-java/docker-java/pull/61) + * [#60](https://github.com/docker-java/docker-java/pull/60) Added additional callback methods to EventCallback + * [#1](https://github.com/docker-java/docker-java/pull/1) Merge + * [#55](https://github.com/docker-java/docker-java/pull/55) + * [#58](https://github.com/docker-java/docker-java/pull/58) Code clear and bug fix + * [#54](https://github.com/docker-java/docker-java/pull/54) + * [#3](https://github.com/docker-java/docker-java/pull/3) + * [#2](https://github.com/docker-java/docker-java/pull/2) Move to new maven coordinate com.github.docker-java:docker-java + * [#1](https://github.com/docker-java/docker-java/pull/1) Merge + * [#34](https://github.com/docker-java/docker-java/pull/34) + * [#36](https://github.com/docker-java/docker-java/pull/36) + * [#37](https://github.com/docker-java/docker-java/pull/37) Starts v0.10.0 + * [#32](https://github.com/docker-java/docker-java/pull/32) + +v0.8.1 +--- + + +v0.8.1 +--- + + * [#28](https://github.com/docker-java/docker-java/pull/28) Improves use of docker-java in unit tests + * [#30](https://github.com/docker-java/docker-java/pull/30) Add ping method + * [#27](https://github.com/docker-java/docker-java/pull/27) Added a close method to DockerClient + * [#26](https://github.com/docker-java/docker-java/pull/26) + * [#24](https://github.com/docker-java/docker-java/pull/24) + * [#23](https://github.com/docker-java/docker-java/pull/23) + * [#22](https://github.com/docker-java/docker-java/pull/22) Fixes for startContainerCmd + * [#20](https://github.com/docker-java/docker-java/pull/20) + * [#19](https://github.com/docker-java/docker-java/pull/19) Add missing options to BuildCmd and set them to match Docker client. + * [#18](https://github.com/docker-java/docker-java/pull/18) Added Container-Linking + * [#16](https://github.com/docker-java/docker-java/pull/16) Build image improvements + * [#15](https://github.com/docker-java/docker-java/pull/15) Collection of changes driven by use in gradle-docker and on Windows + * [#14](https://github.com/docker-java/docker-java/pull/14) Use RegEx to match ADD command from Dockerfile. + * [#9](https://github.com/docker-java/docker-java/pull/9) Add a build command accepting a tar as a InputStream, so we can build the Dockerfile TAR on the fly without a temporary folder. + * [#5](https://github.com/docker-java/docker-java/pull/5) add paused field in ContainerInspectResponse + * [#6](https://github.com/docker-java/docker-java/pull/6) diff --git a/LICENSE b/LICENSE index d6456956..38275f2f 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright [2013] [docker-java@googlegroups.com] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 24cfc9ad..4c77e896 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ -# docker-java +[![Build Status](https://travis-ci.org/docker-java/docker-java.svg?branch=master)](https://travis-ci.org/docker-java/docker-java) +[![Circle CI](https://circleci.com/gh/docker-java/docker-java.svg?style=svg)](https://circleci.com/gh/docker-java/docker-java) +# docker-java Java API client for [Docker](http://docs.docker.io/ "Docker") -Supports a subset of the Docker Client API v1.11, Docker Server version 0.11 +Supports a subset of the Docker Client API v1.18, Docker Server version 1.6.0 + +The current implementation is based on Jersey 2.x and therefore classpath incompatible with older Jersey 1.x dependent libraries! Developer forum for [docker-java](https://groups.google.com/forum/?hl=de#!forum/docker-java-dev "docker-java") @@ -10,136 +14,127 @@ Developer forum for [docker-java](https://groups.google.com/forum/?hl=de#!forum/ ###### Prerequisites: -* Java 1.6+ +* Java 1.7 * Maven 3.0.5 * Docker daemon running -Maven may run tests during build process but tests are disabled by default. The tests are using a localhost instance of Docker, make sure that you have Docker running for tests to work. To run the tests you have to provide your https://www.docker.io/account/login/ information: - - $ mvn clean install -DskipTests=false -Ddocker.io.username=... -Ddocker.io.password=... -Ddocker.io.email=... - -By default Docker server is using UNIX sockets for communication with the Docker client, however docker-java -client uses TCP/IP to connect to the Docker server, so you will need to make sure that your Docker server is -listening on TCP port. To allow Docker server to use TCP add the following line to /etc/default/docker - - DOCKER_OPTS="-H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock" - -More details setting up docket server can be found in official documentation: http://docs.docker.io/en/latest/use/basics/ - -Now make sure that docker is up: - - $ docker -H tcp://127.0.0.1:4243 version - - Client version: 0.8.1 - Go version (client): go1.2 - Git commit (client): a1598d1 - Server version: 0.8.1 - Git commit (server): a1598d1 - Go version (server): go1.2 - Last stable version: 0.8.1 +If you need SSL, then you'll need to put your `*.pem` file into `~/.docker/`, if you're using boot2docker, do this: + + $ ln -s /Users/alex.collins/.boot2docker/certs/boot2docker-vm .docker -Run build with tests: +Build and run integration tests as follows: $ mvn clean install -## Docker-Java maven dependency: - - - com.kpelykh - docker-java - 0.8.1 - - - -## Example code snippets: - - DockerClient dockerClient = new DockerClient("http://localhost:4243"); +If you do not have access to a Docker server or just want to execute the build quickly, you can run the build without the integration tests: -###### Get Docker info: + $ mvn clean install -DskipITs - Info info = dockerClient.info(); - System.out.print(info); - -###### Search Docker repository: - - List dockerSearch = dockerClient.search("busybox"); - System.out.println("Search returned" + dockerSearch.toString()); - -###### Create new Docker container, wait for its start and stop it: +By default Docker server is using UNIX sockets for communication with the Docker client, however docker-java +client uses TCP/IP to connect to the Docker server by default, so you will need to make sure that your Docker server is +listening on TCP port. To allow Docker server to use TCP add the following line to /etc/default/docker - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] {"touch", "/test"}); - ContainerCreateResponse container = dockerClient.createContainer(containerConfig); + DOCKER_OPTS="-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock" - dockerClient.startContainer(container.id); +However you can force docker-java to use UNIX socket communication by configure the following url: - dockerClient.waitContainer(container.id); + unix:///var/run/docker.sock - dockerClient.stopContainer(container.id); +More details setting up Docker server can be found in official documentation: http://docs.docker.io/en/latest/use/basics/ +Now make sure that docker is up: -##### Support for UNIX sockets: + $ docker -H tcp://127.0.0.1:2375 version - Support for UNIX socket should appear in docker-java pretty soon. I'm working on its integration. + Client version: 0.8.0 + Go version (client): go1.2 + Git commit (client): cc3a8c8 + Server version: 1.2.0 + Git commit (server): fa7b24f + Go version (server): go1.3.1 -##### Docker Builder: +Run build without integration tests: -To use Docker Builder, as described on page http://docs.docker.io/en/latest/use/builder/, -user dockerClient.build(baseDir), where baseDir is a path to folder containing Dockerfile. + $ mvn clean install -DskipITs +## Docker-Java maven dependencies - File baseDir = new File("~/kpelykh/docker/netcat"); +### Latest release version - ClientResponse response = dockerClient.build(baseDir); + + com.github.docker-java + docker-java + 1.3.0 + - StringWriter logwriter = new StringWriter(); +### Latest SNAPSHOT version +You can find the latest SNAPSHOT version including javadoc and source files on [Sonatypes OSS repository](https://oss.sonatype.org/content/groups/public/com/github/docker-java/docker-java/). - try { - LineIterator itr = IOUtils.lineIterator(response.getEntityInputStream(), "UTF-8"); - while (itr.hasNext()) { - String line = itr.next(); - logwriter.write(line); - LOG.info(line); - } - } finally { - IOUtils.closeQuietly(response.getEntityInputStream()); - } + + com.github.docker-java + docker-java + 1.3.1-SNAPSHOT + +## Documentation -For additional examples, please look at [DockerClientTest.java](https://github.com/kpelykh/docker-java/blob/master/src/test/java/com/kpelykh/docker/client/test/DockerClientTest.java "DockerClientTest.java") +For code examples, please look at the [Wiki](https://github.com/docker-java/docker-java/wiki) or [Test cases](https://github.com/docker-java/docker-java/tree/master/src/test/java/com/github/dockerjava/core/command "Test cases") ## Configuration There are a couple of configuration items, all of which have sensible defaults: -* `url` The Docker URL, e.g. `http://localhost:4243`. -* `version` The API version, e.g. `1.11`. -* `username` Your repository username (required to push containers). -* `password` Your repository password. -* `email` Your repository email. +* `url` The Docker URL, e.g. `https://localhost:2376` or `unix:///var/run/docker.sock` +* `version` The API version, e.g. `1.16`. +* `username` Your registry username (required to push containers). +* `password` Your registry password. +* `email` Your registry email. +* `serverAddress` Your registry's address. +* `dockerCertPath` Path to the docker certs. There are three ways to configure, in descending order of precedence: -##### Programatic: +#### Programmatic: In your application, e.g. - DockerClient docker = new DockerClient("http://localhost:4243"); - docker.setCredentials("dockeruser", "ilovedocker", "dockeruser@github.com");` + DockerClientConfig config = DockerClientConfig.createDefaultConfigBuilder() + .withVersion("1.16") + .withUri("https://my-docker-host.tld:2376") + .withUsername("dockeruser") + .withPassword("ilovedocker") + .withEmail("dockeruser@github.com") + .withServerAddress("https://index.docker.io/v1/") + .withDockerCertPath("/home/user/.docker") + .build(); + DockerClient docker = DockerClientBuilder.getInstance(config).build(); + +#### Properties + + docker.io.url=https://localhost:2376 + docker.io.version=1.16 + docker.io.username=dockeruser + docker.io.password=ilovedocker + docker.io.email=dockeruser@github.com + docker.io.serverAddress=https://index.docker.io/v1/ + docker.io.dockerCertPath=/home/user/.docker + ##### System Properties: -E.g. - java -Ddocker.io.username=kpelykh pkg.Main + java -Ddocker.io.username=dockeruser pkg.Main -##### File System -In `$HOME/.docker.io.properties`, e.g.: +##### System Environment - docker.io.username=dockeruser + export DOCKER_URL=http://localhost:2376 + +Note: we also auto-detect defaults. If you use `DOCKER_HOST` we use that value, and if `DOCKER_CERT_PATH` or `DOCKER_TLS_VERIFY=1` is set, we switch to SSL. + +##### File System + +In `$HOME/.docker.io.properties` ##### Class Path -In the class path at `/docker.io.properties`, e.g.: - docker.io.url=http://localhost:4243 - docker.io.version=1.11 +In the class path at `/docker.io.properties` + diff --git a/circle.md b/circle.md new file mode 100644 index 00000000..d2d671d8 --- /dev/null +++ b/circle.md @@ -0,0 +1,51 @@ +# docker-java on circleCI + +The build including tests and integration tests can be automatically run on [circleCI](https://circleci.com/). + +## Setup +1. create an account on circle CI using your github account. +2. select docker-java from the github projects listed in your profile. +3. go to the project settings for docker-java (click on the gear-wheel icon beside the docker-java title). +4. open the *Environment variable* page. +5. add the following environment variables: + - DOCKER_EMAIL + - DOCKER_PASSWORD + - DOCKER_USERNAME + +## Ignored Tests +ExecCreateCmdImplTest.execCreateTest + + - Exec is not supported by the lxc driver + +ExecStartCmdImplTest.execStartTest + + - Exec is not supported by the lxc driver + +KillContainerCmdImplTest.killContainer + + - Killed container has ExitCode 0 + +ListImagesCmdImplTest.listDanglingImages + + - caused by [docker#9939](https://github.com/docker/docker/issues/9939) + +RemoveContainerCmdImplTest.removeContainer + + - caused by [docker#9939](https://github.com/docker/docker/issues/9939) + +RemoveImageCmdImplTest.removeImage + + - caused by [docker#9939](https://github.com/docker/docker/issues/9939) + +ContainerDiffCmdImplTest.testContainerDiff + + - too many diffs [{"Kind":0,"Path":"/dev"} ,{"Kind":1,"Path":"/dev/fuse"} ,{"Kind":1,"Path":"/dev/ptmx"} ,{"Kind":1,"Path":"/dev/tty"} ,{"Kind":1,"Path":"/dev/tty1"} ,{"Kind":1,"Path":"/dev/stdout"} ,{"Kind":1,"Path":"/dev/urandom"} ,{"Kind":1,"Path":"/dev/full"} ,{"Kind":1,"Path":"/dev/kmsg"} ,{"Kind":1,"Path":"/dev/null"} ,{"Kind":1,"Path":"/dev/stdin"} ,{"Kind":1,"Path":"/dev/stderr"} ,{"Kind":1,"Path":"/dev/zero"} ,{"Kind":1,"Path":"/dev/fd"} ,{"Kind":1,"Path":"/dev/random"} ,{"Kind":1,"Path":"/test"} ] + +BuildImageCmdImplTest.testDockerIgnore + + - ignore is not working + +StopContainerCmdImplTest.testStopContainer + + - Stopped container has ExitCode 0 + diff --git a/circle.sh b/circle.sh new file mode 100755 index 00000000..c84ca3fe --- /dev/null +++ b/circle.sh @@ -0,0 +1,45 @@ +#!/bin/bash -ex + +case "$1" in + pre_machine) + # copy certificates to default directory ~/.docker + mkdir .docker + cp $CIRCLE_PROJECT_REPONAME/etc/certs/* .docker + + # configure docker deamon to use SSL and provide the path to the certificates + docker_opts='DOCKER_OPTS="$DOCKER_OPTS -H tcp://127.0.0.1:2376 --tlsverify --tlscacert='$HOME'/.docker/ca.pem --tlscert='$HOME'/.docker/server-cert.pem --tlskey='$HOME'/.docker/server-key.pem"' + sudo sh -c "echo '$docker_opts' >> /etc/default/docker" + + # debug output + cat /etc/default/docker + ls -la $HOME/.docker + ;; + + post_machine) + # fix permissions on docker.log so it can be collected as an artifact + sudo chown ubuntu:ubuntu /var/log/upstart/docker.log + + # validate that docker is working + docker version + ;; + + dependencies) + mvn clean install -T 2 -Dmaven.javadoc.skip=true -DskipTests=true -B -V + ;; + + test) + mvn clean verify + ;; + + collect_artifacts) + # collect artifacts into the artifacts dir + cp target/*.jar $CIRCLE_ARTIFACTS + ;; + + collect_test_reports) + mkdir -p $CIRCLE_TEST_REPORTS/surefire + mkdir -p $CIRCLE_TEST_REPORTS/failsafe + cp target/surefire-reports/TEST-*.xml $CIRCLE_TEST_REPORTS/surefire + cp target/failsafe-reports/TEST-*.xml $CIRCLE_TEST_REPORTS/failsafe + ;; +esac diff --git a/circle.yml b/circle.yml new file mode 100644 index 00000000..d8a80371 --- /dev/null +++ b/circle.yml @@ -0,0 +1,28 @@ +dependencies: + override: + - ./circle.sh dependencies + +test: + override: + - ./circle.sh test + post: + - ./circle.sh collect_artifacts + - ./circle.sh collect_test_reports + +machine: + pre: + - ls -la docker-java + - $CIRCLE_PROJECT_REPONAME/circle.sh pre_machine + post: + - $CIRCLE_PROJECT_REPONAME/circle.sh post_machine + services: + - docker + environment: + MAVEN_OPTS: -Xmx128m + DOCKER_HOST: tcp://127.0.0.1:2376 + DOCKER_CERT_PATH: $HOME/.docker + DOCKER_TLS_VERIFY: 1 + +general: + artifacts: + - /var/log/upstart/docker.log diff --git a/etc/certs/README.md b/etc/certs/README.md new file mode 100644 index 00000000..165265a0 --- /dev/null +++ b/etc/certs/README.md @@ -0,0 +1,175 @@ +# Creating Certificates for Docker + +## Warning +> These certificates are only meant for integration tests on CI environments (like circleCI). Do not use them for any real machine. +> Since all keys are publicly available anybody could gain root access to your machine. + +### 1. Create the certificate files +There is an [excellent guide](https://docs.docker.com/articles/https/) on the official docker homepage. +This document contains the log on how the certificates in this folder were created. +It differs slightly form the official guide. + - Certificates are valid for 10 years instead of 1 year. + - Certificates use v3_req extension to support both `127.0.0.1` and `localhost` (see config file [server-cert.txt](server-cert.txt)). + +``` +$ cd ~ +``` + +``` +$ mkdir .docker +``` + +``` +$ cd .docker +``` + +``` +$ echo 01 > ca.srl +``` + +``` +$ openssl genrsa -des3 -out ca-key.pem 2048 +Generating RSA private key, 2048 bit long modulus +............................................+++ +..................+++ +e is 65537 (0x10001) +Enter pass phrase for ca-key.pem: docker-java +Verifying - Enter pass phrase for ca-key.pem: docker-java +``` + +``` +$ openssl req -new -x509 -days 3650 -key ca-key.pem -out ca.pem +Enter pass phrase for ca-key.pem: docker-java +You are about to be asked to enter information that will be incorporated +into your certificate request. +What you are about to enter is what is called a Distinguished Name or a DN. +There are quite a few fields but you can leave some blank +For some fields there will be a default value, +If you enter '.', the field will be left blank. +----- +Country Name (2 letter code) [AU]: +State or Province Name (full name) [Some-State]: +Locality Name (eg, city) []: +Organization Name (eg, company) [Internet Widgits Pty Ltd]: docker-java +Organizational Unit Name (eg, section) []: +Common Name (e.g. server FQDN or YOUR name) []: +Email Address []: +``` + +``` +$ openssl genrsa -des3 -out server-key.pem 2048 +Generating RSA private key, 2048 bit long modulus +..........+++ +.........+++ +e is 65537 (0x10001) +Enter pass phrase for server-key.pem: docker-java +Verifying - Enter pass phrase for server-key.pem: docker-java +``` + +``` +$ openssl req -new -key server-key.pem -out server.csr -config server-cert.txt +Enter pass phrase for server-key.pem: docker-java +You are about to be asked to enter information that will be incorporated +into your certificate request. +What you are about to enter is what is called a Distinguished Name or a DN. +There are quite a few fields but you can leave some blank +For some fields there will be a default value, +If you enter '.', the field will be left blank. +----- +TypeCommonNameHere []: localhost +``` + +``` +$ openssl x509 -req -days 3650 -in server.csr -CA ca.pem -CAkey ca-key.pem -out server-cert.pem -extensions v3_req -extfile server-cert.txt +Signature ok +subject=/CN=localhost +Getting CA Private Key +Enter pass phrase for ca-key.pem: docker-java +``` + +``` +$ openssl genrsa -des3 -out key.pem 2048 +Generating RSA private key, 2048 bit long modulus +............................................+++ +.......................................................+++ +e is 65537 (0x10001) +Enter pass phrase for key.pem: docker-java +Verifying - Enter pass phrase for key.pem: docker-java +``` + +``` +$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr +Enter pass phrase for key.pem: docker-java +``` + +``` +$ echo extendedKeyUsage = clientAuth > extfile.cnf +``` + +``` +$ openssl x509 -req -days 3650 -in client.csr -CA ca.pem -CAkey ca-key.pem -out cert.pem -extfile extfile.cnf +Signature ok +subject=/CN=client +Getting CA Private Key +Enter pass phrase for ca-key.pem: docker-java +``` + +``` +$ openssl rsa -in server-key.pem -out server-key.pem +Enter pass phrase for server-key.pem: docker-java +writing RSA key +``` + +``` +$ openssl rsa -in key.pem -out key.pem +Enter pass phrase for key.pem: docker-java +writing RSA key +``` + +Once you created all the files you can have a look at their content with the following command + +``` +openssl x509 -in .pem -inform pem -noout -text +``` + +### 2. Configuring the docker daemon +On linux the docker daemon allows to specify options in the file `/etc/default/docker`. +By adding the following line (or modifying an existing line) one can get the docker daemon to listen on both *unix socket* and *https*. +``` +DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://127.0.0.1:2376 --tlsverify --tlscacert=~/.docker/ca.pem --tlscert=~/.docker/server-cert.pem --tlskey=~/.docker/server-key.pem" +``` + +### 3. Restart the daemon and test the setup +After changing the daemon options it must be restarted + +``` +$ sudo service docker restart +``` + +To test the socket and the https connection: + +``` +$ docker -H tcp://127.0.0.1:2376 --tlsverify version +Client version: 1.4.1 +Client API version: 1.16 +Go version (client): go1.3.3 +Git commit (client): 5bc2ff8 +OS/Arch (client): linux/amd64 +Server version: 1.4.1 +Server API version: 1.16 +Go version (server): go1.3.3 +Git commit (server): 5bc2ff8 +``` + +``` +$ docker -H unix:///var/run/docker.sock version +Client version: 1.4.1 +Client API version: 1.16 +Go version (client): go1.3.3 +Git commit (client): 5bc2ff8 +OS/Arch (client): linux/amd64 +Server version: 1.4.1 +Server API version: 1.16 +Go version (server): go1.3.3 +Git commit (server): 5bc2ff8 +``` diff --git a/etc/certs/ca-key.pem b/etc/certs/ca-key.pem new file mode 100644 index 00000000..bf1b0e2d --- /dev/null +++ b/etc/certs/ca-key.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,A5E7732EBEA97F20 + +/KgdVVIFc++tLxcot9FCziUqi6uZMk2pFy676Vh9ZILuHE5t8WZ66sn+aXjsNb0T +L9Z67+Yx9E3Y7L2wl/NwwpYZctt9XWo8wR1fVucYf98PEbRWXlbS1GYK3bMtYPeU +iGxLOK/WU3uqItH9g2uxCeXf+wjP14kiFrzrrd1XrShxTUs/bqIbHd7+GJetgvpS +5UKXiISLUf+5EwuODVhe6zYg80P616HuitkKDekAM7PZz6YsVIhPNnxOlSIEIylS +sPK71Aiy6yu57Qp6Jm1E1JsPVcoEOAEtAUlMxxyVNhj5fQ35lX/OkkhJGXmKXfIl +U6YVBreAwSeuwv7ochmyIsoOHzw5QJGlm8ycMYaxrceh7J3cGT0+04WgLi9+ieZ/ +AseXPryryqhIgR4qN0/9vAnA++N+YtelzhIeXqJhtpMEc5yb7po/OJejMboFJyAa +5P4IvCXohfSVSMBwaB2Vh9xA+2d34hQS5AKFkzlfbMJU81HmCUh70CKYJ9PGSdh4 +itkSoc9iQ2e0RxOxhJSqjpYYbKcxL2PTkMTpxqDGiJQ8FfLWuDhpz5mUWSvt3a41 +zbypHsKntibKP8Ceem1vckIid0mnWaZolbOcHBzErsa/3BvO5ZP3q7HTf35yyy7o +VO1LXQqO8trYnkG2PiXk0eWUzTVgiGQYFoGdAdFOkF95y2LBRcgyBhQTfxIbqMW3 +Nr2wkSHU0JT1NLI0UKhVAB7/uLJKwGgLcYU9j4DAA4rT8sZF9zbdjLuSFx5dnV+h +g8H+QOKbvmGpvTIeDrxwSpgzu7mqmPcpf1gFHBTdYCV8QXF4u96VZlar6dnoYoVj +OdS96HYxC2gsrDJJ5Nls0deTWqdKxbnDRXs4Vu7yTO1B/nMnSgptksOoVq50SD1a +IrtA4d6aZzfr1U647nWXTdaeDJ2acq4u8fM8JZNbMj0/Y7HJa3BQz+55yCQb+iFE +1VOzm1D9xcU8NCBgquT4C6FcPIQQpyq50B7lez8SEyAvqPnoMlqAulF6qo+0ZtZC +MaS+C2dlNUUjVOV7qDsracSY6esf5WF40nc1jzNflV+Pwqk7ISDkv4GiiH+yXq8t +hrP2sAiWHd2oHw1RCoEW7UgWAyW72wAOFTwnvQBCkVmxk/kBgrNDU+8SBmClqSod +CEWOcb6apore20xXmhq8mEddW2Xjk+U1dLRkBBp5oaopbYQ25LO+PZIpIJr1mcCi +6D42EHmCGQovkaL4G40IhXDrU98u2J72be8i/B7sQ8s3A5d7CwJwICG5PoiM7MJU +dfwWPmpKVAt38PQ4fWmZjw3pUnU2qyXGUc5TV6J8ZhU5yFvx4P7n7/Zd1QaZ34ar +A3XcHoAidu8O/C1N/bdSttJHniirTIYYgiqVTtjT66qALSeDCab3JT9C7Zfxr39+ +YamKTXu+dMw7C91Cg9ETiNcNvNsjVK1cMVmcwirJjSlK1Wr1cRbFkWg55uVSuO52 +r08qGAHge9gekW1Rcc/RKp0qv193PYCYMzKUoW28MEspHPbs84fhGIiK8v/OF9Pg +nu0T8r+Xzr2a7TSSTsO77WTjXrEfgjrddfUhsIiSNWVePL86iNkZoB5QrWH7DgKW +-----END RSA PRIVATE KEY----- diff --git a/etc/certs/ca.pem b/etc/certs/ca.pem new file mode 100644 index 00000000..0cf1b2b9 --- /dev/null +++ b/etc/certs/ca.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQzCCAiugAwIBAgIJAN94VLe4RxmkMA0GCSqGSIb3DQEBCwUAMDgxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRQwEgYDVQQKDAtkb2NrZXItamF2 +YTAeFw0xNTAxMjAyMTE5MzBaFw0yNTAxMTcyMTE5MzBaMDgxCzAJBgNVBAYTAkFV +MRMwEQYDVQQIDApTb21lLVN0YXRlMRQwEgYDVQQKDAtkb2NrZXItamF2YTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANiZy9f+AHejo9s9Ve1WwXQeKR28 +s2SbSpf82+kMtLHL0/r0HIDwhA9uOSglqpFY21vVWFYGY+Mh8Z4bgoyUOFIM/WQn +fgS2trYjfRZuZeR8u57dVyqLyzMV7eWdPBbnU1rTavwvsQMwWWwKF+AXhKpUoDtT +N+gQyqTQYdEsyhQS/HzC7fH3QC0deSrUS5yjX9qIEr3dZwE93BK2wqUFEy9JvyQ5 +DIh5N6HpRKzFcH/zHd/GPeXXnWKVCO5plDn/XTv85a5PesyFV2Zi//0g3UWgpzFC +2sTh5Brj7kamlVbOLBpQTV2LFY38c3MNYbiNTzKtkAQgVtHA4YS5ym3z8PcCAwEA +AaNQME4wHQYDVR0OBBYEFJME9p4vpy900Ryw/E/Mz750ZK9xMB8GA1UdIwQYMBaA +FJME9p4vpy900Ryw/E/Mz750ZK9xMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBANZPR1572nkOKEsxZkV1jou64NqZLGGVjXEdjJP56R5G8cyoBoZfqiT0 +J2/IxU7UZ5n65C79FLeLnAq/0GuWw07BREzAZjBZmBOe3Ma3qZi47MESIEEJ06XL +g4/rGFbeDlV9otVhKQ27U//4qodLUNzZBq+VrQNUfSk8vqEb0iGKHSSLTV6DZx8w +Yb79ZFA53+LOloKKbrGQsSKshSDUxnl+19jVXMUgvvrcyEE20I30wB0ZVE5pPME7 +tAqBFjqe+SR+gxwVXrpRSPhjZfqDhSOvsbZwuJqNVqE1sVnZCbwcnvIGGRu8TWej +JARvyGHOlwoE3kVH+4VKeYQ7PcL5wck= +-----END CERTIFICATE----- diff --git a/etc/certs/ca.srl b/etc/certs/ca.srl new file mode 100644 index 00000000..75016ea3 --- /dev/null +++ b/etc/certs/ca.srl @@ -0,0 +1 @@ +03 diff --git a/etc/certs/cert.pem b/etc/certs/cert.pem new file mode 100644 index 00000000..7063ef99 --- /dev/null +++ b/etc/certs/cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC2zCCAcOgAwIBAgIBAzANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEUMBIGA1UECgwLZG9ja2VyLWphdmEwHhcNMTUw +MTIwMjEzNjIzWhcNMjUwMTE3MjEzNjIzWjARMQ8wDQYDVQQDDAZjbGllbnQwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZlY8JUY4VqGyi/h9b0WHvqjgt +y+nyQd9vdgY6IvjsLDcl0fjp4knFxSpl1U0oLA1JnElT3Qgv1zv4GYvc/DUbld7d +5B1jQzOYbUWgOzRa/903wpEXn9waBU2mwtyObzmXvmZ6XUA+fgx3EFnimflzEkjm +vYskH2lW0pbbt4/3cCeD282DEmmzaOOkFR8agV6aE4T/x+BRFkVgR1T8Dfiu6Yyy +CJ57OO7kcCGoAPIEe3dGi5UGQVKuDjlohZKApg6E2ISINVvbxsHqKkGU9lNRqxkg +P6qV8LAhypo5sEqeAvlnIgOrhpb0c+zzoqgV0d+s2gU2GVa6SvT0OcidwyzjAgMB +AAGjFzAVMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQCL +2T/pu5weRuXcPEvBvmyOfEZZp0bzfGw0DYsp5XKARV7M6qJPPTpjqLm3AbjPqsv2 +fn0GQq8hWThveJhYps6mnhAsYC5rlfxFH+JC2i1EfhTIjWCP7WKdjXJr6hmhrVNc +7cxyQjWh/vSAk4nRcH3fNLUF1HNhZBuB1aOniTWTeoTgd7sIQjVpX4V6avOEcSGo +mylt3fqHO5X/CONFJnvv0SHFOCGr9WX/9Sq0GlpHoEK80gBVQt/O22U1PGZ2kYaT +Z/Fs2r85ltC/POPTCyQ3oKk4j2YF7HwJhJ2gP8jwKl4aX1iypwpCa/d9hvCFn8Wh +bEv9E557EH9PjLyqlnwi +-----END CERTIFICATE----- diff --git a/etc/certs/client.csr b/etc/certs/client.csr new file mode 100644 index 00000000..44748724 --- /dev/null +++ b/etc/certs/client.csr @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICVjCCAT4CAQAwETEPMA0GA1UEAwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA2ZWPCVGOFahsov4fW9Fh76o4Lcvp8kHfb3YGOiL47Cw3 +JdH46eJJxcUqZdVNKCwNSZxJU90IL9c7+BmL3Pw1G5Xe3eQdY0MzmG1FoDs0Wv/d +N8KRF5/cGgVNpsLcjm85l75mel1APn4MdxBZ4pn5cxJI5r2LJB9pVtKW27eP93An +g9vNgxJps2jjpBUfGoFemhOE/8fgURZFYEdU/A34rumMsgieezju5HAhqADyBHt3 +RouVBkFSrg45aIWSgKYOhNiEiDVb28bB6ipBlPZTUasZID+qlfCwIcqaObBKngL5 +ZyIDq4aW9HPs86KoFdHfrNoFNhlWukr09DnIncMs4wIDAQABoAAwDQYJKoZIhvcN +AQELBQADggEBAJ+IARXkgVRRj2sARH4D+1cwD5WSZwlTr5dsB5myGlNx7crNrWeu +Rqhu2r3xh8b5FlIbggC+aR8U5ikeBHfiRYXV3lt41+AKriKqNR348AAw/WLWPEDG +V6WWieVyicTdN2yMH3tWcisIsqTiVGWDcagVhRmAPP0VBdQ8OyTJCIjIiOhG0LDg +LlEiFuO7npc5ywXJjfh4TjFsyfSjTJv3UYY7zWYgfalWit9OeDbMhgGOwOwOFyDj +lOpQKeBxDwhnH5DC90OmbjK4FkMbQhrTXvURfUts/wz2SLqkQVB8FWuVc0RwUhnX +s2KWbehobjOpK3WkOgDfLGY72HnhU5IbP9Q= +-----END CERTIFICATE REQUEST----- diff --git a/etc/certs/extfile.cnf b/etc/certs/extfile.cnf new file mode 100644 index 00000000..74dedb38 --- /dev/null +++ b/etc/certs/extfile.cnf @@ -0,0 +1 @@ +extendedKeyUsage = clientAuth diff --git a/etc/certs/key.pem b/etc/certs/key.pem new file mode 100644 index 00000000..4319223e --- /dev/null +++ b/etc/certs/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA2ZWPCVGOFahsov4fW9Fh76o4Lcvp8kHfb3YGOiL47Cw3JdH4 +6eJJxcUqZdVNKCwNSZxJU90IL9c7+BmL3Pw1G5Xe3eQdY0MzmG1FoDs0Wv/dN8KR +F5/cGgVNpsLcjm85l75mel1APn4MdxBZ4pn5cxJI5r2LJB9pVtKW27eP93Ang9vN +gxJps2jjpBUfGoFemhOE/8fgURZFYEdU/A34rumMsgieezju5HAhqADyBHt3RouV +BkFSrg45aIWSgKYOhNiEiDVb28bB6ipBlPZTUasZID+qlfCwIcqaObBKngL5ZyID +q4aW9HPs86KoFdHfrNoFNhlWukr09DnIncMs4wIDAQABAoIBAAT2o2+r+5jE4c2K +DH8UtK457NQmnayYEhfB0nyLBbClhZCITKxRLCulxsTR69OpxZfTR9zw4tnsiAKt +2oHtAu0hKxdWt9Dm3Itymq8ACr60rYAzIQov7F5vlojiUxOwt9idUEskS23hhDlH +FzXTG1yjoyQYWM83JDkFzskuU8tqKcdZUGHxwDqWaLpCJRCqaXGBbwBsiBE0WpD4 +U2Iq32Cbcf2KK/iI2CpEhEVSLZAHuh1Ovv4l2kb3PSFV1x4zSOJjHO6LM+4nAgmx +1XcVH+Qpw7oRoRpEajBss6XlIvRRohhXRlzUDi5T6smkoo69pXn1OFaav6U8BwVN +2nIrnJECgYEA8Wn9OBnxgJvVji+Wdc92IoHgSB8Y/xPuI4a2go84AHd0Ek7GzE6g +o52eq1v+7+Uj2y/xTIvcGEfOz8/oeOzWJqsLpi34CeqhewwWHNogAD2Xg6DpCHmc +ARzKKod9djwU0y5+/8YBsfpSM6ZKzbnP5HzblA+oIllAY50VJvp+HhsCgYEA5rr8 +8EsjnuNo+/RuVp4vATP2ML6D/ILHQEZR6tPgfjaiEZfMm22yX6J8F/kNVHO2c1Pt +94A+dKa3QyQBieb27u1+N0iUOkAYvnExkaazkVwOH4sXwn4EzxGFTdwwSW5Uenl4 +8Qry08mnnJwKP38TlNrw3Q9PbZNFFlXLdaAteNkCgYEAjhmFI3Ch5sHTgk4gklf7 +gXRRQCKN5BcnJWI2K8OBg0TM3bng6oGdrLEqpFMSTMLjyDEAJ75rXx9lbI1EmWlO +5JPp13dXeP7S7kq++VyrWXjnpmMgyAxM95qqpT37a40R9Px5ZLR4avqdSCmp/k5R +QHz+Arj0jsNaU0gzGy527eECgYEAu1Mk+JKIoP9QxP52dqyGzRs6zehfkCs9SdnN +uDG9GbuRaWctHyO4phxtU6lIQUCQ6KFDmqXsDxkrwL8C4Ms7wE/hIVEzxvczH6sW +64HEWEe8z69F4wodLWC0MbqLGZMR0z28CzcwXvJhuqyVRWgOpdP7qf48JmJivPd6 +8EzTnkECgYA5x137xZgFZGYOdRF4yCLkbZ8weDKbcTotUgNSFR2xMYzytzWmEg5D +rvjhGXmhqvxNTzEJehWVVJoQHFW0aJhC7ML2VP56DQ7p2gM75HrojZpdoUzwF73T ++E6W/Nddm6N8tuQ/q/rIrsgF6/FJ9H5D9ZeK69En3QwC4xUgHlWQGA== +-----END RSA PRIVATE KEY----- diff --git a/etc/certs/server-cert.pem b/etc/certs/server-cert.pem new file mode 100644 index 00000000..1edc7b79 --- /dev/null +++ b/etc/certs/server-cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC5TCCAc2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEUMBIGA1UECgwLZG9ja2VyLWphdmEwHhcNMTUw +MTIwMjEzMTQ4WhcNMjUwMTE3MjEzMTQ4WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3Qw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDECxOHu9oVQX2IGivToPdC +gcVcewBOQJClLDmnGdfP99gtBdNaa59cXnPudd+r24lfnIs98XUbAWZoYFYTmj1s +jukyL1jwH2zcPVmcTKWz6zTbComxaNQJRHZGPIeR1qJIiJHIMa0Xt9y0w/SH0qHw +Ts69PkRCILaUGRhsms5qYJ91fsRzYO4jXYR3F0vEEoUzhlI7qw4n3QaTOVbZ154v +3VEvlrECCLVpE4KUcccgenNNoEeqTdkiAe4ywupHjkzo3ThcWSfv+a+oRRmQlhsD +BPL2RxyNiu2NvML6WyMPKQNvyV7s6kyDKmpbLg3Y0QV/VhneZ90+bPG/VslpMPzb +AgMBAAGjHjAcMBoGA1UdEQQTMBGHBH8AAAGCCWxvY2FsaG9zdDANBgkqhkiG9w0B +AQsFAAOCAQEAInNXInEzAA9AbEOsNXKyAXHJQWCUK6+tCQkxNIiGKaRWtEYLmrpx +1xx7xTACZihpK+vRyG4MUNkHIoVqNi/Jg4Z0cJq3lU3FnCpU0TUY2avj8iWbn2Ke +B5SFnPyi2JSZhw8ZsssA6/cs7EoTbQYSi1CO7SoKXj8RvQJcYWc8dx3Ydkq05pIT +e897LxrHjrpiGK+a+2ghw9amfnXmElYneKP5WrpZcIiHAD3Fp6ecvYkLGLf1G5D/ +bjjMUfbsYMjeZFVG6wzTJ8tBDeJkovpb2vPpuLM+raYkqGDiUBD2Kqgyq1uL6ZLc +PYmsXVGiosG7QpVxDsaYy8DQa/4FeQWC1g== +-----END CERTIFICATE----- diff --git a/etc/certs/server-cert.txt b/etc/certs/server-cert.txt new file mode 100644 index 00000000..4ce34a14 --- /dev/null +++ b/etc/certs/server-cert.txt @@ -0,0 +1,13 @@ +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[req_distinguished_name] +commonName = TypeCommonNameHere + +[v3_req] +subjectAltName = @alt_names + +[alt_names] +IP.1 = 127.0.0.1 +DNS.1 = localhost diff --git a/etc/certs/server-key.pem b/etc/certs/server-key.pem new file mode 100644 index 00000000..7490cbcb --- /dev/null +++ b/etc/certs/server-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxAsTh7vaFUF9iBor06D3QoHFXHsATkCQpSw5pxnXz/fYLQXT +WmufXF5z7nXfq9uJX5yLPfF1GwFmaGBWE5o9bI7pMi9Y8B9s3D1ZnEyls+s02wqJ +sWjUCUR2RjyHkdaiSIiRyDGtF7fctMP0h9Kh8E7OvT5EQiC2lBkYbJrOamCfdX7E +c2DuI12EdxdLxBKFM4ZSO6sOJ90GkzlW2deeL91RL5axAgi1aROClHHHIHpzTaBH +qk3ZIgHuMsLqR45M6N04XFkn7/mvqEUZkJYbAwTy9kccjYrtjbzC+lsjDykDb8le +7OpMgypqWy4N2NEFf1YZ3mfdPmzxv1bJaTD82wIDAQABAoIBAEpDT+CpHpHSvzTh +hRyj60Z8VkEULyd1edW4DRbpyUD0yCU2AbxM7I9XEF+Ss2osvbtEV9LdNtlGDH8Z +j0HZuc73zArAuNYtcVV0wA9fUZ34SYt4UHSuGzRvhMZSg6CRR+RmJ6NIfiNv+OFZ +IbaZ6dJYaCR0A/Nw5PjxELmDYxIUYcXKZAwjVP+GLvOmjE/xSD1kwEVeIzmtgEeX +UixqI8KgDpASuf0N21fJ67pcAIohrmsWzjeSXMFINFCMuUuqAKCuPpUthybHjMy/ +7vjEI7ViEcpcHd7IW5h+RTl3r4hnQaNbbbcwGWmc+8u7qOcPJe6cEBXD8ud6dZUz +wdLdRwECgYEA8z9Vw/Ms2NQwQA4FUcnoQQ4pnK5bJIPLAa7MaomEYfVciMU1oWcI +4gspTelHSSY4+D/AlyLD5FgDpi0YfeYQJjcZVs2m/5mZn5BFqQKAmmMWtBxo6RSO +VF6SyfNsqpQTsFY0dp9HKCDVo0mtTKFizLBDrmSCroGv3fb6KodH25sCgYEAzlI0 +3Lan70ABn3cO4iGfQvvrUw2A/ZX01lVtDJzq8LJ+IXy9zev4C+ZOUT/8/L/D4K3W +hO1px7WrUSaMr5F9scXC0PYfMNoMn9YzfxEYCMQA5dCWnUhf6t2ESUFeh1Qt3b7j +kz7U2/pSk7VXithwITKKEgicG0EBABHuyio0l8ECgYEAjF90YwwmSUrKPWzZ7QUT +ntdJdD26IyxbLrFtDd4mI1GxAMyt0mLfYXMHdwq0NKZ2IezIe294lIGmOXO0upLV +pvgNC2bKhJ5jZQ2g5ZOoG3ArXe03LarLKC0bkKeFgjrJ0e0tgXcRXTr1jrGp7JsF +pRHjPPSL5aC5mOI6I+jFsxkCgYAV6uG2uKiqX9BMUmeAWjYC9aQQFJUpWy7BPh3x +gxHqM+v2PrwjDfgxu2uCchu79dsnGRB62oWsM35ZhpDXbcs+gVWqwRqbI+7HZAfg +bb5x3/CAeWImnzOhTZrp9UnHcofU0Jx4Xepa3AK9sjv0gf2XaTkFpWh/9K3Yhg8p +5sXjgQKBgGv3a1VpQ0YG3PoN7l8Pjzg6/IYslxUkZwdyUqeTJD3hRDbQybXIhJGw +0GbAkxTkXwFFQpRplCj5wk6U0M/JZRW3IhiuHovmZd/WSvl3EgzLT/5U3kYE3K6V +ZH1Hgs1SCQhQXIWm/QerI1KlNg4PKhctPohSc1ttW3hQUBh1reTR +-----END RSA PRIVATE KEY----- diff --git a/etc/certs/server.csr b/etc/certs/server.csr new file mode 100644 index 00000000..f4fc6d38 --- /dev/null +++ b/etc/certs/server.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIChjCCAW4CAQAwFDESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAxAsTh7vaFUF9iBor06D3QoHFXHsATkCQpSw5pxnX +z/fYLQXTWmufXF5z7nXfq9uJX5yLPfF1GwFmaGBWE5o9bI7pMi9Y8B9s3D1ZnEyl +s+s02wqJsWjUCUR2RjyHkdaiSIiRyDGtF7fctMP0h9Kh8E7OvT5EQiC2lBkYbJrO +amCfdX7Ec2DuI12EdxdLxBKFM4ZSO6sOJ90GkzlW2deeL91RL5axAgi1aROClHHH +IHpzTaBHqk3ZIgHuMsLqR45M6N04XFkn7/mvqEUZkJYbAwTy9kccjYrtjbzC+lsj +DykDb8le7OpMgypqWy4N2NEFf1YZ3mfdPmzxv1bJaTD82wIDAQABoC0wKwYJKoZI +hvcNAQkOMR4wHDAaBgNVHREEEzARhwR/AAABgglsb2NhbGhvc3QwDQYJKoZIhvcN +AQELBQADggEBAGaf5ZlZMuUmLzdyWQEm90YPu9jpqrFocu2GUmHLEuW23/p3JRHX +gljn8VJkHaaAtP8gqPnk6Fn2BZJZrqVsjg+KoLxHuKUC0K2ZdYoFBl4usmfpQqe9 +3F0qUvScHaqdxff6OKORD1hXqCYF6V1WGwb2cTSbYr1xy/ehK+VhhPd+zTaQg0Es +CcacEJTYd61gIsW2FD0XkokUUokNLj0FL9dt2ANgaefJPKBH/Pp/2hTiXEhEjIm6 +MkQVjnMbx/5/5k48yhpwPEl0FdiyLQdbzT03sHbrDKCeAdOJgOibZUnJYCJ8/1vu +AH28ajlCUMvKsUE+mtDU/VYvci758n5CiYc= +-----END CERTIFICATE REQUEST----- diff --git a/pom.xml b/pom.xml index 3de239d1..f8eb34ce 100644 --- a/pom.xml +++ b/pom.xml @@ -1,305 +1,393 @@ - - 4.0.0 - - - org.sonatype.oss - oss-parent - 7 - - - com.kpelykh - docker-java - jar - 0.8.2-SNAPSHOT - - docker-java - https://github.com/kpelykh/docker-java - Java API Client for Docker - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - scm:git:git@github.com:kpelykh/docker-java.git - git@github.com:kpelykh/docker-java.git - scm:git:git@github.com:kpelykh/docker-java.git - - - - - kpelykh - Konstantin Pelykh - kpelykh@gmail.com - - - - - true - - UTF-8 - true - false - 1.6 - 1.6 - - 1.6.1 - - 1.18 - 1.9 - - 2.3.3 - - 4.2.5 - 1.5 - 2.3 - 2.6 - 1.7.5 - 1.3.9 - 0.3 - - - 1.0.1 - 5.12.1 - 1.3 - 1.6 - 2.3.3 - - - 2.2 - 2.3.1 - 2.3.1 - 2.8.1 - 2.5.1 - 1.7 - - - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - ${jackson-jaxrs.version} - - - com.sun.jersey - jersey-core - ${jersey.version} - - - com.sun.jersey - jersey-client - ${jersey.version} - - - - com.sun.jersey.contribs - jersey-multipart - ${jersey.version} - - - com.sun.jersey.contribs - jersey-apache-client4 - ${jersey-apache-client4.version} - - - - org.apache.httpcomponents - httpclient - ${httpclient.version} - - - - org.apache.commons - commons-compress - ${commons-compress.version} - - - commons-lang - commons-lang - ${commons-lang.version} - - - commons-io - commons-io - ${commons-io.version} - - - - com.github.jnr - jnr-unixsocket - ${jnr.unixsocket.version} - - - - org.slf4j - slf4j-api - ${slf4j-api.version} - - - - org.slf4j - jul-to-slf4j - ${slf4j-api.version} - - - - - ch.qos.logback - logback-core - ${version.logback} - test - - - - ch.qos.logback - logback-classic - ${version.logback} - test - - - - org.testng - testng - ${version.testng} - test - - - - org.hamcrest - hamcrest-library - ${hamcrest.library.version} - test - - - - com.googlecode.lambdaj - lambdaj - ${lambdaj.version} - test - - - org.hamcrest - hamcrest-all - - - - - - org.testinfected.hamcrest-matchers - jpa-matchers - ${hamcrest.jpa-matchers} - test - - - - - - com.github.jnr - jffi - 1.2.7 - - - com.github.jnr - jffi - native - 1.2.7 - - - - - - - - - - - org.apache.maven.plugins - maven-release-plugin - ${maven-release-plugin.version} - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${jdk.source} - ${jdk.target} - ISO-8859-1 - ${jdk.debug} - ${jdk.optimize} - - - - - org.apache.maven.plugins - maven-jar-plugin - ${maven-jar-plugin.version} - - - - test-jar - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - ${skipTests} - - - - - - org.codehaus.mojo - cobertura-maven-plugin - ${cobertura-maven-plugin.version} - - - - org.apache.maven.plugins - maven-antrun-plugin - ${maven-antrun-plugin.version} - - - validate - - run - - - - ******************************************************************* - ******************************************************************* - [project.name] : ${project.name} - [project.basedir] : ${project.basedir} - [project.version] : ${project.version} - [project.artifactId] ${project.artifactId} - [project.build.directory] ${project.build.directory} - [jdk.source] : ${jdk.source} - [jdk.target] : ${jdk.target} - [jdk.debug] : ${jdk.debug} - [jdk.optimize] : ${jdk.optimize} - [source encoding]: ${project.build.sourceEncoding} - [LocalRepository] : ${settings.localRepository} - ******************************************************************* - ******************************************************************* - - - - - - - - - + + 4.0.0 + + + org.sonatype.oss + oss-parent + 9 + + + com.github.docker-java + docker-java + jar + 1.3.1-SNAPSHOT + + docker-java + https://github.com/docker-java/docker-java + Java API Client for Docker + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + scm:git:git@github.com:docker-java/docker-java.git + git@github.com:docker-java/docker-java.git + scm:git:git@github.com:docker-java/docker-java.git + HEAD + + + + + kpelykh + Konstantin Pelykh + kpelykh@gmail.com + + + + + UTF-8 + UTF-8 + true + false + 1.7 + 1.7 + + 2.11 + 2.1.2 + 4.3.1 + 1.5 + 1.8 + 2.3 + 2.6 + 1.7.5 + + 1.51 + 2015-01-27T15-02-14 + 18.0 + + + 1.1.0 + 5.12.1 + 1.3 + 1.6 + 2.3.3 + + + 2.2 + 2.3.1 + 2.3.1 + 2.17 + 2.17 + 2.5.1 + 1.7 + + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${jackson-jaxrs.version} + + + org.glassfish.jersey.connectors + jersey-apache-connector + ${jersey.version} + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + org.glassfish.jersey.core + jersey-client + ${jersey.version} + + + de.gesellix + unix-socket-factory + ${unix-socket-factory.version} + + + + org.apache.commons + commons-compress + ${commons-compress.version} + + + commons-codec + commons-codec + ${commons-codec.version} + + + commons-lang + commons-lang + ${commons-lang.version} + + + commons-io + commons-io + ${commons-io.version} + + + org.slf4j + slf4j-api + ${slf4j-api.version} + + + com.google.guava + guava + ${guava.version} + + + org.bouncycastle + bcpkix-jdk15on + ${bouncycastle.version} + + + + + ch.qos.logback + logback-core + ${logback.version} + test + + + + ch.qos.logback + logback-classic + ${logback.version} + test + + + + org.testng + testng + ${testng.version} + test + + + + org.hamcrest + hamcrest-library + ${hamcrest.library.version} + test + + + + com.googlecode.lambdaj + lambdaj + ${lambdaj.version} + test + + + org.hamcrest + hamcrest-all + + + + + + org.testinfected.hamcrest-matchers + jpa-matchers + ${hamcrest.jpa-matchers} + test + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + + + + org.apache.maven.plugins + maven-release-plugin + ${maven-release-plugin.version} + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${jdk.source} + ${jdk.target} + ${jdk.debug} + ${jdk.optimize} + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + test-jar + + + + + + + org.codehaus.mojo + cobertura-maven-plugin + ${cobertura-maven-plugin.version} + + + + org.apache.maven.plugins + maven-antrun-plugin + ${maven-antrun-plugin.version} + + + validate + + run + + + + ******************************************************************* + ******************************************************************* + [project.name] : ${project.name} + [project.basedir] : ${project.basedir} + [project.version] : ${project.version} + [project.artifactId] ${project.artifactId} + [project.build.directory] ${project.build.directory} + [jdk.source] : ${jdk.source} + [jdk.target] : ${jdk.target} + [jdk.debug] : ${jdk.debug} + [jdk.optimize] : ${jdk.optimize} + [source encoding]: ${project.build.sourceEncoding} + [LocalRepository] : ${settings.localRepository} + ******************************************************************* + ******************************************************************* + + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.2 + true + + ossrh + https://oss.sonatype.org/ + true + + + + + org.apache.maven.plugins + maven-release-plugin + 2.5 + + true + false + release + deploy nexus-staging:release + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + integration + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + + integration-test + verify + + + integration + ignoreInCircleCi + + **/*Test.java + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + + + + + release + + + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-source-plugin + + + + + diff --git a/src/main/java/com/github/dockerjava/api/BadRequestException.java b/src/main/java/com/github/dockerjava/api/BadRequestException.java new file mode 100644 index 00000000..1e04bcb3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/BadRequestException.java @@ -0,0 +1,23 @@ +package com.github.dockerjava.api; + + +/** + * + */ +public class BadRequestException extends DockerException { + + private static final long serialVersionUID = -2450396075981100160L; + + public BadRequestException(String message, Throwable cause) { + super(message, 400, cause); + } + + public BadRequestException(String message) { + this(message, null); + } + + public BadRequestException(Throwable cause) { + this(cause.getMessage(), cause); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/ConflictException.java b/src/main/java/com/github/dockerjava/api/ConflictException.java new file mode 100644 index 00000000..bed2a375 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/ConflictException.java @@ -0,0 +1,23 @@ +package com.github.dockerjava.api; + + +/** + * + */ +public class ConflictException extends DockerException { + + private static final long serialVersionUID = -290093024775500239L; + + public ConflictException(String message, Throwable cause) { + super(message, 409, cause); + } + + public ConflictException(String message) { + this(message, null); + } + + public ConflictException(Throwable cause) { + this(cause.getMessage(), cause); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/DockerClient.java b/src/main/java/com/github/dockerjava/api/DockerClient.java new file mode 100644 index 00000000..0e141176 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/DockerClient.java @@ -0,0 +1,122 @@ +package com.github.dockerjava.api; + +import java.io.*; + +import com.github.dockerjava.api.command.*; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.Identifier; + +// https://godoc.org/github.com/fsouza/go-dockerclient +public interface DockerClient extends Closeable { + + public AuthConfig authConfig() throws DockerException; + + /** + * Authenticate with the server, useful for checking authentication. + */ + public AuthCmd authCmd(); + + public InfoCmd infoCmd(); + + public PingCmd pingCmd(); + + public VersionCmd versionCmd(); + + /** + * * IMAGE API * + */ + + public PullImageCmd pullImageCmd(String repository); + + public PushImageCmd pushImageCmd(String name); + + public PushImageCmd pushImageCmd(Identifier identifier); + + public CreateImageCmd createImageCmd(String repository, + InputStream imageStream); + + public SearchImagesCmd searchImagesCmd(String term); + + public RemoveImageCmd removeImageCmd(String imageId); + + public ListImagesCmd listImagesCmd(); + + public InspectImageCmd inspectImageCmd(String imageId); + + public SaveImageCmd saveImageCmd(String name); + + /** + * * CONTAINER API * + */ + + public ListContainersCmd listContainersCmd(); + + public CreateContainerCmd createContainerCmd(String image); + + /** + * Creates a new {@link StartContainerCmd} for the container with the + * given ID. + * The command can then be further customized by using builder + * methods on it like {@link StartContainerCmd#withDns(String...)}. + *

+ * If you customize the command, any existing configuration of the + * target container will get reset to its default before applying the + * new configuration. To preserve the existing configuration, use an + * unconfigured {@link StartContainerCmd}. + *

+ * This command corresponds to the /containers/{id}/start + * endpoint of the Docker Remote API. + */ + public StartContainerCmd startContainerCmd(String containerId); + + public ExecCreateCmd execCreateCmd(String containerId); + + public InspectContainerCmd inspectContainerCmd(String containerId); + + public RemoveContainerCmd removeContainerCmd(String containerId); + + public WaitContainerCmd waitContainerCmd(String containerId); + + public AttachContainerCmd attachContainerCmd(String containerId); + + public ExecStartCmd execStartCmd(String containerId); + + public InspectExecCmd inspectExecCmd(String execId); + + public LogContainerCmd logContainerCmd(String containerId); + + public CopyFileFromContainerCmd copyFileFromContainerCmd( + String containerId, String resource); + + public ContainerDiffCmd containerDiffCmd(String containerId); + + public StopContainerCmd stopContainerCmd(String containerId); + + public KillContainerCmd killContainerCmd(String containerId); + + public RestartContainerCmd restartContainerCmd(String containerId); + + public StatsCmd statsCmd(String containerId); + + public CommitCmd commitCmd(String containerId); + + public BuildImageCmd buildImageCmd(); + + public BuildImageCmd buildImageCmd(File dockerFileOrFolder); + + public BuildImageCmd buildImageCmd(InputStream tarInputStream); + + public TopContainerCmd topContainerCmd(String containerId); + + public TagImageCmd tagImageCmd(String imageId, String repository, String tag); + + public PauseContainerCmd pauseContainerCmd(String containerId); + + public UnpauseContainerCmd unpauseContainerCmd(String containerId); + + public EventsCmd eventsCmd(EventCallback eventCallback); + + public void close() throws IOException; + + +} diff --git a/src/main/java/com/github/dockerjava/api/DockerClientException.java b/src/main/java/com/github/dockerjava/api/DockerClientException.java new file mode 100644 index 00000000..85efc349 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/DockerClientException.java @@ -0,0 +1,18 @@ +package com.github.dockerjava.api; + +/** + * + * + */ +public class DockerClientException extends RuntimeException { + +private static final long serialVersionUID = 7667768099261650608L; + + public DockerClientException(String message) { + super(message); + } + + public DockerClientException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/github/dockerjava/api/DockerException.java b/src/main/java/com/github/dockerjava/api/DockerException.java new file mode 100644 index 00000000..673bf2cc --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/DockerException.java @@ -0,0 +1,30 @@ +package com.github.dockerjava.api; + + + + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ + +public class DockerException extends RuntimeException { + +private static final long serialVersionUID = 7667768099261650608L; + + private int httpStatus = 0; + + public DockerException(String message, int httpStatus) { + super(message); + this.httpStatus = httpStatus; + } + + public DockerException(String message, int httpStatus, Throwable cause) { + super(message, cause); + } + + public int getHttpStatus() { + return httpStatus; + } +} diff --git a/src/main/java/com/github/dockerjava/api/InternalServerErrorException.java b/src/main/java/com/github/dockerjava/api/InternalServerErrorException.java new file mode 100644 index 00000000..366484e5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/InternalServerErrorException.java @@ -0,0 +1,22 @@ +package com.github.dockerjava.api; + +/** + * + */ +public class InternalServerErrorException extends DockerException { + + private static final long serialVersionUID = -2450396075981100160L; + + public InternalServerErrorException(String message, Throwable cause) { + super(message, 500, cause); + } + + public InternalServerErrorException(String message) { + this(message, null); + } + + public InternalServerErrorException(Throwable cause) { + this(cause.getMessage(), cause); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/NotAcceptableException.java b/src/main/java/com/github/dockerjava/api/NotAcceptableException.java new file mode 100644 index 00000000..affa4ce9 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/NotAcceptableException.java @@ -0,0 +1,22 @@ +package com.github.dockerjava.api; + +/** + * + */ +public class NotAcceptableException extends DockerException { + + private static final long serialVersionUID = -1771212181727204375L; + + public NotAcceptableException(String message, Throwable cause) { + super(message, 406, cause); + } + + public NotAcceptableException(String message) { + this(message, null); + } + + public NotAcceptableException(Throwable cause) { + this(cause.getMessage(), cause); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/NotFoundException.java b/src/main/java/com/github/dockerjava/api/NotFoundException.java new file mode 100644 index 00000000..10f328cd --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/NotFoundException.java @@ -0,0 +1,23 @@ +package com.github.dockerjava.api; + +/** + * Indicates that the given entity does not exist. + * + * @author Ryan Campbell ryan.campbell@gmail.com + */ +public class NotFoundException extends DockerException { + + private static final long serialVersionUID = -2450396075981100160L; + + public NotFoundException(String message, Throwable cause) { + super(message, 404, cause); + } + + public NotFoundException(String message) { + this(message, null); + } + + public NotFoundException(Throwable cause) { + this(cause.getMessage(), cause); + } +} diff --git a/src/main/java/com/github/dockerjava/api/NotModifiedException.java b/src/main/java/com/github/dockerjava/api/NotModifiedException.java new file mode 100644 index 00000000..d521bae2 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/NotModifiedException.java @@ -0,0 +1,22 @@ +package com.github.dockerjava.api; + +/** + * + */ +public class NotModifiedException extends DockerException { + + private static final long serialVersionUID = -290093024775500239L; + + public NotModifiedException(String message, Throwable cause) { + super(message, 304, cause); + } + + public NotModifiedException(String message) { + this(message, null); + } + + public NotModifiedException(Throwable cause) { + this(cause.getMessage(), cause); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/UnauthorizedException.java b/src/main/java/com/github/dockerjava/api/UnauthorizedException.java new file mode 100644 index 00000000..5d48d216 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/UnauthorizedException.java @@ -0,0 +1,22 @@ +package com.github.dockerjava.api; + +/** + * + */ +public class UnauthorizedException extends DockerException { + + private static final long serialVersionUID = 8257731964780578278L; + + public UnauthorizedException(String message, Throwable cause) { + super(message, 401, cause); + } + + public UnauthorizedException(String message) { + this(message, null); + } + + public UnauthorizedException(Throwable cause) { + this(cause.getMessage(), cause); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/command/AttachContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/AttachContainerCmd.java new file mode 100644 index 00000000..0fb72d45 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/AttachContainerCmd.java @@ -0,0 +1,81 @@ +package com.github.dockerjava.api.command; + +import java.io.InputStream; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.NotFoundException; + +/** + * Attach to container + * + * @param logs + * - true or false, includes logs. Defaults to false. + * + * @param followStream + * - true or false, return stream. Defaults to false. + * @param stdout + * - true or false, includes stdout log. Defaults to false. + * @param stderr + * - true or false, includes stderr log. Defaults to false. + * @param timestamps + * - true or false, if true, print timestamps for every log line. + * Defaults to false. + */ +public interface AttachContainerCmd extends DockerCmd { + + public String getContainerId(); + + public boolean hasLogsEnabled(); + + public boolean hasFollowStreamEnabled(); + + public boolean hasTimestampsEnabled(); + + public boolean hasStdoutEnabled(); + + public boolean hasStderrEnabled(); + + public AttachContainerCmd withContainerId(String containerId); + + /** + * See {@link #withFollowStream(boolean)} + */ + public AttachContainerCmd withFollowStream(); + + /** + * Following the stream means the resulting {@link InputStream} returned by + * {@link #exec()} reads infinitely. So a {@link InputStream#read()} MAY + * BLOCK FOREVER as long as no data is streamed from the docker host to + * {@link DockerClient}! + */ + public AttachContainerCmd withFollowStream(boolean followStream); + + public AttachContainerCmd withTimestamps(boolean timestamps); + + public AttachContainerCmd withStdOut(); + + public AttachContainerCmd withStdOut(boolean stdout); + + public AttachContainerCmd withStdErr(); + + public AttachContainerCmd withStdErr(boolean stderr); + + public AttachContainerCmd withLogs(boolean logs); + + public AttachContainerCmd withLogs(); + + /** + * Its the responsibility of the caller to consume and/or close the + * {@link InputStream} to prevent connection leaks. + * + * @throws NotFoundException + * No such container + */ + @Override + public InputStream exec() throws NotFoundException; + + public static interface Exec extends + DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/AuthCmd.java b/src/main/java/com/github/dockerjava/api/command/AuthCmd.java new file mode 100644 index 00000000..3c592258 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/AuthCmd.java @@ -0,0 +1,29 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.UnauthorizedException; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.AuthResponse; + +/** + * + * Authenticate with the server, useful for checking authentication. + * + */ +public interface AuthCmd extends DockerCmd { + + public AuthConfig getAuthConfig(); + + public AuthCmd withAuthConfig(AuthConfig authConfig); + + /** + * @return The status. Based on it's value you may mean you need to authorise your account, e.g.: + * "Account created. Please see the documentation of the registry http://localhost:5000/v1/ for instructions how to activate it." + * @throws UnauthorizedException If you're not authorised (e.g. bad password). + */ + @Override + public AuthResponse exec() throws UnauthorizedException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java b/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java new file mode 100644 index 00000000..925946db --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java @@ -0,0 +1,71 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.model.AuthConfigurations; +import com.github.dockerjava.api.model.EventStreamItem; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * + * Build an image from Dockerfile. + * + * TODO: http://docs.docker.com/reference/builder/#dockerignore + * + */ +public interface BuildImageCmd extends DockerCmd{ + + public BuildImageCmd withTag(String tag); + + public InputStream getTarInputStream(); + + public String getTag(); + + public boolean hasNoCacheEnabled(); + + public boolean hasRemoveEnabled(); + + public boolean isQuiet(); + + public boolean hasPullEnabled(); + + public String getPathToDockerfile(); + + public AuthConfigurations getBuildAuthConfigs(); + + public BuildImageCmd withBaseDirectory(File baseDirectory); + + public BuildImageCmd withDockerfile(File dockerfile); + + public BuildImageCmd withTarInputStream(InputStream tarInputStream); + + public BuildImageCmd withNoCache(); + + public BuildImageCmd withNoCache(boolean noCache); + + public BuildImageCmd withRemove(); + + public BuildImageCmd withRemove(boolean rm); + + public BuildImageCmd withQuiet(); + + public BuildImageCmd withQuiet(boolean quiet); + + public BuildImageCmd withPull(); + + public BuildImageCmd withPull(boolean pull); + + public BuildImageCmd withBuildAuthConfigs(AuthConfigurations authConfig); + + public static interface Exec extends DockerCmdExec { + } + + /** + * @see {@link com.github.dockerjava.core.command.EventStreamReader} + */ + public static abstract class Response extends InputStream { + public abstract Iterable getItems() throws IOException; + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/CommitCmd.java b/src/main/java/com/github/dockerjava/api/command/CommitCmd.java new file mode 100644 index 00000000..ac0a7bb5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/CommitCmd.java @@ -0,0 +1,115 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.model.ExposedPorts; +import com.github.dockerjava.api.model.Volumes; + +/** +* +* Create a new image from a container's changes. Returns the new image ID. +* +*/ +public interface CommitCmd extends DockerCmd{ + + public String getContainerId(); + + public CommitCmd withContainerId(String containerId); + + public String getRepository(); + + public String getTag(); + + public String getMessage(); + + public String getAuthor(); + + public boolean hasPauseEnabled(); + + public CommitCmd withAttachStderr(boolean attachStderr); + + public CommitCmd withAttachStderr(); + + public CommitCmd withAttachStdin(boolean attachStdin); + + public CommitCmd withAttachStdin(); + + public CommitCmd withAttachStdout(boolean attachStdout); + + public CommitCmd withAttachStdout(); + + public CommitCmd withCmd(String... cmd); + + public CommitCmd withDisableNetwork(boolean disableNetwork); + + public CommitCmd withAuthor(String author); + + public CommitCmd withMessage(String message); + + public CommitCmd withTag(String tag); + + public CommitCmd withRepository(String repository); + + public CommitCmd withPause(boolean pause); + + public String[] getEnv(); + + public CommitCmd withEnv(String... env); + + public ExposedPorts getExposedPorts(); + + public CommitCmd withExposedPorts(ExposedPorts exposedPorts); + + public String getHostname(); + + public CommitCmd withHostname(String hostname); + + public Integer getMemory(); + + public CommitCmd withMemory(Integer memory); + + public Integer getMemorySwap(); + + public CommitCmd withMemorySwap(Integer memorySwap); + + public boolean isOpenStdin(); + + public CommitCmd withOpenStdin(boolean openStdin); + + public String[] getPortSpecs(); + + public CommitCmd withPortSpecs(String... portSpecs); + + public boolean isStdinOnce(); + + public CommitCmd withStdinOnce(boolean stdinOnce); + + public CommitCmd withStdinOnce(); + + public boolean isTty(); + + public CommitCmd withTty(boolean tty); + + public CommitCmd withTty(); + + public String getUser(); + + public CommitCmd withUser(String user); + + public Volumes getVolumes(); + + public CommitCmd withVolumes(Volumes volumes); + + public String getWorkingDir(); + + public CommitCmd withWorkingDir(String workingDir); + + /** + * @throws NotFoundException No such container + */ + @Override + public String exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/ContainerDiffCmd.java b/src/main/java/com/github/dockerjava/api/command/ContainerDiffCmd.java new file mode 100644 index 00000000..21d99045 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/ContainerDiffCmd.java @@ -0,0 +1,30 @@ +package com.github.dockerjava.api.command; + +import java.util.List; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.InternalServerErrorException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.model.ChangeLog; + +public interface ContainerDiffCmd extends DockerCmd> { + + public String getContainerId(); + + public ContainerDiffCmd withContainerId(String containerId); + + @Override + public String toString(); + + /** + * @throws NotFoundException No such container + * @throws InternalServerErrorException server error + * @throws DockerException unexpected http status code + */ + @Override + public List exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec> { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/CopyFileFromContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/CopyFileFromContainerCmd.java new file mode 100644 index 00000000..5e85d998 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/CopyFileFromContainerCmd.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.api.command; + +import java.io.InputStream; + +import com.github.dockerjava.api.NotFoundException; + +public interface CopyFileFromContainerCmd extends DockerCmd { + + public String getContainerId(); + + public String getResource(); + + public CopyFileFromContainerCmd withContainerId(String containerId); + + public CopyFileFromContainerCmd withResource(String resource); + + public String getHostPath(); + + public CopyFileFromContainerCmd withHostPath(String hostPath); + + /** + * Its the responsibility of the caller to consume and/or close the {@link InputStream} to prevent + * connection leaks. + * + * @throws NotFoundException No such container + */ + @Override + public InputStream exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java new file mode 100644 index 00000000..708e3139 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java @@ -0,0 +1,245 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.ConflictException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Capability; +import com.github.dockerjava.api.model.Device; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.Link; +import com.github.dockerjava.api.model.LxcConf; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.api.model.RestartPolicy; +import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumesFrom; +import com.github.dockerjava.core.command.CreateContainerCmdImpl; + +public interface CreateContainerCmd extends DockerCmd{ + + public static interface Exec extends DockerCmdExec { + } + + /** + * @throws NotFoundException No such container + * @throws ConflictException Named container already exists + */ + @Override + public CreateContainerResponse exec() throws NotFoundException, + ConflictException; + + public Bind[] getBinds(); + + public Capability[] getCapAdd(); + + public Capability[] getCapDrop(); + + public String[] getCmd(); + + public String getCpuset(); + + public int getCpuShares(); + + public Device[] getDevices(); + + public String[] getDns(); + + public String[] getDnsSearch(); + + public String[] getEntrypoint(); + + public String[] getEnv(); + + public ExposedPort[] getExposedPorts(); + + public String[] getExtraHosts(); + + public HostConfig getHostConfig(); + + public String getHostName(); + + public String getImage(); + + public Link[] getLinks(); + + public LxcConf[] getLxcConf(); + + public String getMacAddress(); + + public long getMemoryLimit(); + + public long getMemorySwap(); + + public String getName(); + + public String getNetworkMode(); + + public Ports getPortBindings(); + + public String[] getPortSpecs(); + + public RestartPolicy getRestartPolicy(); + + public String getUser(); + + public Volume[] getVolumes(); + + public VolumesFrom[] getVolumesFrom(); + + public String getWorkingDir(); + + public boolean isAttachStderr(); + + public boolean isAttachStdin(); + + public boolean isAttachStdout(); + + public boolean isNetworkDisabled(); + + public Boolean isPrivileged(); + + public Boolean isPublishAllPorts(); + + public boolean isStdInOnce(); + + public boolean isStdinOpen(); + + public boolean isTty(); + + public CreateContainerCmd withAttachStderr(boolean attachStderr); + + public CreateContainerCmd withAttachStdin(boolean attachStdin); + + public CreateContainerCmd withAttachStdout(boolean attachStdout); + + public CreateContainerCmd withBinds(Bind... binds); + + /** + * Add linux kernel + * capability to the container. For example: adding {@link Capability#MKNOD} + * allows the container to create special files using the 'mknod' command. + */ + public CreateContainerCmd withCapAdd(Capability... capAdd); + + /** + * Drop linux kernel + * capability from the container. For example: dropping {@link Capability#CHOWN} + * prevents the container from changing the owner of any files. + */ + public CreateContainerCmd withCapDrop(Capability... capDrop); + + public CreateContainerCmd withCmd(String... cmd); + + public CreateContainerCmd withCpuset(String cpuset); + + public CreateContainerCmd withCpuShares(int cpuShares); + + /** + * Add host devices to the container + */ + public CreateContainerCmd withDevices(Device... devices); + + public CreateContainerCmd withNetworkDisabled(boolean disableNetwork); + + /** + * Set custom DNS servers + */ + public CreateContainerCmd withDns(String... dns); + + /** + * Set custom DNS search domains + */ + public CreateContainerCmd withDnsSearch(String... dnsSearch); + + public CreateContainerCmd withEntrypoint(String... entrypoint); + + public CreateContainerCmd withEnv(String... env); + + public CreateContainerCmd withExposedPorts(ExposedPort... exposedPorts); + + /** + * Add hostnames to /etc/hosts in the container + */ + public CreateContainerCmd withExtraHosts(String... extraHosts); + + public CreateContainerCmd withHostConfig(HostConfig hostConfig); + + public CreateContainerCmd withHostName(String hostName); + + public CreateContainerCmd withImage(String image); + + /** + * Add link to another container. + */ + public CreateContainerCmd withLinks(Link... links); + + public CreateContainerCmd withLxcConf(LxcConf... lxcConf); + + public CreateContainerCmd withMemoryLimit(long memoryLimit); + + public CreateContainerCmd withMemorySwap(long memorySwap); + + public CreateContainerCmd withName(String name); + + + /** + * Set the Network mode for the container + *

    + *
  • 'bridge': creates a new network stack for the container on the docker + * bridge
  • + *
  • 'none': no networking for this container
  • + *
  • 'container:': reuses another container network stack
  • + *
  • 'host': use the host network stack inside the container. Note: the + * host mode gives the container full access to local system services such + * as D-bus and is therefore considered insecure.
  • + *
+ */ + public CreateContainerCmd withNetworkMode(String networkMode); + + /** + * Add one or more {@link PortBinding}s. + * This corresponds to the --publish (-p) + * option of the docker run CLI command. + */ + public CreateContainerCmd withPortBindings(PortBinding... portBindings); + + /** + * Add the port bindings that are contained in the given {@link Ports} + * object. + * + * @see #withPortBindings(PortBinding...) + */ + public CreateContainerCmd withPortBindings(Ports portBindings); + + public CreateContainerCmd withPortSpecs(String... portSpecs); + + public CreateContainerCmd withPrivileged(boolean privileged); + + public CreateContainerCmd withPublishAllPorts(boolean publishAllPorts); + + /** + * Set custom {@link RestartPolicy} for the container. Defaults to + * {@link RestartPolicy#noRestart()} + */ + public CreateContainerCmd withRestartPolicy(RestartPolicy restartPolicy); + + public CreateContainerCmd withStdInOnce(boolean stdInOnce); + + public CreateContainerCmd withStdinOpen(boolean stdinOpen); + + public CreateContainerCmd withTty(boolean tty); + + public CreateContainerCmd withUser(String user); + + public CreateContainerCmd withVolumes(Volume... volumes); + + public CreateContainerCmd withVolumesFrom(VolumesFrom... volumesFrom); + + public CreateContainerCmd withWorkingDir(String workingDir); + + public CreateContainerCmd withMacAddress(String macAddress); + +} diff --git a/src/main/java/com/kpelykh/docker/client/model/ContainerCreateResponse.java b/src/main/java/com/github/dockerjava/api/command/CreateContainerResponse.java similarity index 67% rename from src/main/java/com/kpelykh/docker/client/model/ContainerCreateResponse.java rename to src/main/java/com/github/dockerjava/api/command/CreateContainerResponse.java index 8f60ce15..5b0b5a53 100644 --- a/src/main/java/com/kpelykh/docker/client/model/ContainerCreateResponse.java +++ b/src/main/java/com/github/dockerjava/api/command/CreateContainerResponse.java @@ -1,45 +1,41 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Arrays; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ContainerCreateResponse { - - @JsonProperty("Id") - private String id; - - @JsonProperty("Warnings") - private String[] warnings; - - public String getId() { - return id; - } - - public String[] getWarnings() { - return warnings; - } - - public void setId(String id) { - this.id = id; - } - - public void setWarnings(String[] warnings) { - this.warnings = warnings; - } - - @Override - public String toString() { - return "ContainerCreateResponse{" + - "id='" + id + '\'' + - ", warnings=" + Arrays.toString(warnings) + - '}'; - } -} +package com.github.dockerjava.api.command; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang.builder.ToStringBuilder; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class CreateContainerResponse { + + @JsonProperty("Id") + private String id; + + @JsonProperty("Warnings") + private String[] warnings; + + public String getId() { + return id; + } + + public String[] getWarnings() { + return warnings; + } + + public void setId(String id) { + this.id = id; + } + + public void setWarnings(String[] warnings) { + this.warnings = warnings; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/command/CreateImageCmd.java b/src/main/java/com/github/dockerjava/api/command/CreateImageCmd.java new file mode 100644 index 00000000..d2682471 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/CreateImageCmd.java @@ -0,0 +1,34 @@ +package com.github.dockerjava.api.command; + +import java.io.InputStream; + +public interface CreateImageCmd extends DockerCmd { + + public String getRepository(); + + // TODO remove method + public String getTag(); + + public InputStream getImageStream(); + + /** + * @param repository the repository to import to + */ + public CreateImageCmd withRepository(String repository); + + /** + * @param imageStream the InputStream of the tar file + */ + public CreateImageCmd withImageStream(InputStream imageStream); + + /** + * @param tag any tag for this image + * @deprecated use repo:tag format for repository + */ + public CreateImageCmd withTag(String tag); + + public static interface Exec extends DockerCmdExec { + } + + +} \ No newline at end of file diff --git a/src/main/java/com/kpelykh/docker/client/model/ImageCreateResponse.java b/src/main/java/com/github/dockerjava/api/command/CreateImageResponse.java similarity index 66% rename from src/main/java/com/kpelykh/docker/client/model/ImageCreateResponse.java rename to src/main/java/com/github/dockerjava/api/command/CreateImageResponse.java index 21545a2d..43731703 100644 --- a/src/main/java/com/kpelykh/docker/client/model/ImageCreateResponse.java +++ b/src/main/java/com/github/dockerjava/api/command/CreateImageResponse.java @@ -1,30 +1,27 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Parse reponses from /images/create - * - * @author Ryan Campbell (ryan.campbell@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ImageCreateResponse { - - @JsonProperty("status") - private String id; - - - public String getId() { - return id; - } - - - @Override - public String toString() { - return "ContainerCreateResponse{" + - "id='" + id + '\'' + - '}'; - } -} +package com.github.dockerjava.api.command; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang.builder.ToStringBuilder; + +/** + * Parse reponses from /images/create + * + * @author Ryan Campbell (ryan.campbell@gmail.com) + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class CreateImageResponse { + + @JsonProperty("status") + private String id; + + public String getId() { + return id; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/command/DockerCmd.java b/src/main/java/com/github/dockerjava/api/command/DockerCmd.java new file mode 100644 index 00000000..89c31666 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/DockerCmd.java @@ -0,0 +1,9 @@ +package com.github.dockerjava.api.command; + +import java.io.Closeable; + +public interface DockerCmd extends Closeable { + + public RES_T exec(); + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/DockerCmdExec.java b/src/main/java/com/github/dockerjava/api/command/DockerCmdExec.java new file mode 100644 index 00000000..8cf13e7b --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/DockerCmdExec.java @@ -0,0 +1,7 @@ +package com.github.dockerjava.api.command; + +public interface DockerCmdExec, RES_T> { + + public RES_T exec(CMD_T command); + +} diff --git a/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java b/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java new file mode 100644 index 00000000..8d54a394 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java @@ -0,0 +1,87 @@ +package com.github.dockerjava.api.command; + +import java.io.Closeable; +import java.io.IOException; + +import com.github.dockerjava.core.DockerClientConfig; + +public interface DockerCmdExecFactory extends Closeable { + + public void init(DockerClientConfig dockerClientConfig); + + public AuthCmd.Exec createAuthCmdExec(); + + public InfoCmd.Exec createInfoCmdExec(); + + public PingCmd.Exec createPingCmdExec(); + + public ExecCreateCmd.Exec createExecCmdExec(); + + public VersionCmd.Exec createVersionCmdExec(); + + public PullImageCmd.Exec createPullImageCmdExec(); + + public PushImageCmd.Exec createPushImageCmdExec(); + + public SaveImageCmd.Exec createSaveImageCmdExec(); + + public CreateImageCmd.Exec createCreateImageCmdExec(); + + public SearchImagesCmd.Exec createSearchImagesCmdExec(); + + public RemoveImageCmd.Exec createRemoveImageCmdExec(); + + public ListImagesCmd.Exec createListImagesCmdExec(); + + public InspectImageCmd.Exec createInspectImageCmdExec(); + + public ListContainersCmd.Exec createListContainersCmdExec(); + + public CreateContainerCmd.Exec createCreateContainerCmdExec(); + + public StartContainerCmd.Exec createStartContainerCmdExec(); + + public InspectContainerCmd.Exec createInspectContainerCmdExec(); + + public RemoveContainerCmd.Exec createRemoveContainerCmdExec(); + + public WaitContainerCmd.Exec createWaitContainerCmdExec(); + + public AttachContainerCmd.Exec createAttachContainerCmdExec(); + + public ExecStartCmd.Exec createExecStartCmdExec(); + + public InspectExecCmd.Exec createInspectExecCmdExec(); + + public LogContainerCmd.Exec createLogContainerCmdExec(); + + public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec(); + + public StopContainerCmd.Exec createStopContainerCmdExec(); + + public ContainerDiffCmd.Exec createContainerDiffCmdExec(); + + public KillContainerCmd.Exec createKillContainerCmdExec(); + + public RestartContainerCmd.Exec createRestartContainerCmdExec(); + + public CommitCmd.Exec createCommitCmdExec(); + + public BuildImageCmd.Exec createBuildImageCmdExec(); + + public TopContainerCmd.Exec createTopContainerCmdExec(); + + public TagImageCmd.Exec createTagImageCmdExec(); + + public PauseContainerCmd.Exec createPauseContainerCmdExec(); + + public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec(); + + public EventsCmd.Exec createEventsCmdExec(); + + public void close() throws IOException; + + // add + public StatsCmd.Exec createStatsCmdExec(); + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/EventCallback.java b/src/main/java/com/github/dockerjava/api/command/EventCallback.java new file mode 100644 index 00000000..18b8669e --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/EventCallback.java @@ -0,0 +1,13 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.model.Event; + +/** + * Event callback + */ +public interface EventCallback { + public void onEvent(Event event); + public void onException(Throwable throwable); + public void onCompletion(int numEvents); + public boolean isReceiving(); +} diff --git a/src/main/java/com/github/dockerjava/api/command/EventsCmd.java b/src/main/java/com/github/dockerjava/api/command/EventsCmd.java new file mode 100644 index 00000000..cfdb23a6 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/EventsCmd.java @@ -0,0 +1,27 @@ +package com.github.dockerjava.api.command; + +import java.util.concurrent.ExecutorService; + + +/** + * Get events + * + * @param since - Show all events created since timestamp + * @param until - Stream events until this timestamp + */ +public interface EventsCmd extends DockerCmd { + public EventsCmd withSince(String since); + + public EventsCmd withUntil(String until); + + public String getSince(); + + public String getUntil(); + + public EventCallback getEventCallback(); + + public EventsCmd withEventCallback(EventCallback eventCallback); + + public static interface Exec extends DockerCmdExec { + } +} diff --git a/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java b/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java new file mode 100644 index 00000000..c1f32e29 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java @@ -0,0 +1,37 @@ +package com.github.dockerjava.api.command; + +public interface ExecCreateCmd extends DockerCmd { + + public String getContainerId(); + + public ExecCreateCmd withContainerId(String containerId); + + public ExecCreateCmd withCmd(String... cmd); + + public ExecCreateCmd withAttachStdin(boolean attachStdin); + + public ExecCreateCmd withAttachStdin(); + + public boolean hasAttachStdinEnabled(); + + public ExecCreateCmd withAttachStdout(boolean attachStdout); + + public ExecCreateCmd withAttachStdout(); + + public boolean hasAttachStdoutEnabled(); + + public ExecCreateCmd withAttachStderr(boolean attachStderr); + + public ExecCreateCmd withAttachStderr(); + + public boolean hasAttachStderrEnabled(); + + public ExecCreateCmd withTty(boolean tty); + + public ExecCreateCmd withTty(); + + public boolean hasTtyEnabled(); + + public static interface Exec extends DockerCmdExec { + } +} diff --git a/src/main/java/com/github/dockerjava/api/command/ExecCreateCmdResponse.java b/src/main/java/com/github/dockerjava/api/command/ExecCreateCmdResponse.java new file mode 100644 index 00000000..391625cf --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/ExecCreateCmdResponse.java @@ -0,0 +1,15 @@ +package com.github.dockerjava.api.command; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class ExecCreateCmdResponse { + + @JsonProperty("Id") + private String id; + + public String getId() { + return id; + } +} diff --git a/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java b/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java new file mode 100644 index 00000000..7ccb90cf --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/ExecStartCmd.java @@ -0,0 +1,38 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +import java.io.InputStream; + +public interface ExecStartCmd extends DockerCmd { + + public String getExecId(); + + public ExecStartCmd withExecId(String execId); + + public boolean hasDetachEnabled(); + + public ExecStartCmd withDetach(boolean detach); + + public ExecStartCmd withDetach(); + + public boolean hasTtyEnabled(); + + public ExecStartCmd withTty(boolean tty); + + public ExecStartCmd withTty(); + + /** + * Its the responsibility of the caller to consume and/or close the {@link InputStream} to prevent + * connection leaks. + * + * @throws com.github.dockerjava.api.NotFoundException + * No such exec instance + */ + @Override + public InputStream exec() throws NotFoundException; + + public static interface Exec extends + DockerCmdExec { + } +} diff --git a/src/main/java/com/github/dockerjava/api/command/InfoCmd.java b/src/main/java/com/github/dockerjava/api/command/InfoCmd.java new file mode 100644 index 00000000..d340fe26 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/InfoCmd.java @@ -0,0 +1,10 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.model.Info; + +public interface InfoCmd extends DockerCmd { + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/InspectContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/InspectContainerCmd.java new file mode 100644 index 00000000..f67fe4ae --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/InspectContainerCmd.java @@ -0,0 +1,20 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +public interface InspectContainerCmd extends DockerCmd { + + public String getContainerId(); + + public InspectContainerCmd withContainerId(String containerId); + + /** + * @throws NotFoundException No such container + */ + @Override + public InspectContainerResponse exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java b/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java new file mode 100644 index 00000000..f9565689 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java @@ -0,0 +1,251 @@ +package com.github.dockerjava.api.command; + + +import java.util.List; +import java.util.Map; + +import com.github.dockerjava.api.model.*; +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class InspectContainerResponse { + + @JsonProperty("Args") + private String[] args; + + @JsonProperty("Config") + private ContainerConfig config; + + @JsonProperty("Created") + private String created; + + @JsonProperty("Driver") + private String driver; + + @JsonProperty("ExecDriver") + private String execDriver; + + @JsonProperty("HostConfig") + private HostConfig hostConfig; + + @JsonProperty("HostnamePath") + private String hostnamePath; + + @JsonProperty("HostsPath") + private String hostsPath; + + @JsonProperty("Id") + private String id; + + @JsonProperty("Image") + private String imageId; + + @JsonProperty("MountLabel") + private String mountLabel; + + @JsonProperty("Name") + private String name; + + @JsonProperty("NetworkSettings") + private NetworkSettings networkSettings; + + @JsonProperty("Path") + private String path; + + @JsonProperty("ProcessLabel") + private String processLabel; + + @JsonProperty("ResolvConfPath") + private String resolvConfPath; + + @JsonProperty("ExecIDs") + private List execIds; + + @JsonProperty("State") + private ContainerState state; + + @JsonProperty("Volumes") + private VolumeBinds volumes; + + @JsonProperty("VolumesRW") + private VolumesRW volumesRW; + + public String getId() { + return id; + } + + public String getCreated() { + return created; + } + + public String getPath() { + return path; + } + + public String getProcessLabel() { + return processLabel; + } + + public String[] getArgs() { + return args; + } + + public ContainerConfig getConfig() { + return config; + } + + public ContainerState getState() { + return state; + } + + public String getImageId() { + return imageId; + } + + public NetworkSettings getNetworkSettings() { + return networkSettings; + } + + public String getResolvConfPath() { + return resolvConfPath; + } + + @JsonIgnore + public VolumeBind[] getVolumes() { + return volumes.getBinds(); + } + + @JsonIgnore + public VolumeRW[] getVolumesRW() { + return volumesRW.getVolumesRW(); + } + + public String getHostnamePath() { + return hostnamePath; + } + + public String getHostsPath() { + return hostsPath; + } + + public String getName() { + return name; + } + + public String getDriver() { + return driver; + } + + public HostConfig getHostConfig() { + return hostConfig; + } + + public String getExecDriver() { + return execDriver; + } + + public String getMountLabel() { + return mountLabel; + } + + public List getExecIds() { + return execIds; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public class NetworkSettings { + + @JsonProperty("IPAddress") private String ipAddress; + @JsonProperty("IPPrefixLen") private int ipPrefixLen; + @JsonProperty("Gateway") private String gateway; + @JsonProperty("Bridge") private String bridge; + @JsonProperty("PortMapping") private Map> portMapping; + @JsonProperty("Ports") private Ports ports; + + public String getIpAddress() { + return ipAddress; + } + + public int getIpPrefixLen() { + return ipPrefixLen; + } + + public String getGateway() { + return gateway; + } + + public String getBridge() { + return bridge; + } + + public Map> getPortMapping() { + return portMapping; + } + + public Ports getPorts() { + return ports; + } + + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public class ContainerState { + + @JsonProperty("Running") private boolean running; + @JsonProperty("Paused") private boolean paused; + @JsonProperty("Pid") private int pid; + @JsonProperty("ExitCode") private int exitCode; + @JsonProperty("StartedAt") private String startedAt; + @JsonProperty("FinishedAt") private String finishedAt; + + public boolean isRunning() { + return running; + } + + public boolean isPaused() { + return paused; + } + + public int getPid() { + return pid; + } + + public int getExitCode() { + return exitCode; + } + + public String getStartedAt() { + return startedAt; + } + + public String getFinishedAt() { + return finishedAt; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } + +} + diff --git a/src/main/java/com/github/dockerjava/api/command/InspectExecCmd.java b/src/main/java/com/github/dockerjava/api/command/InspectExecCmd.java new file mode 100644 index 00000000..13882b8f --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/InspectExecCmd.java @@ -0,0 +1,16 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +public interface InspectExecCmd extends DockerCmd { + public String getExecId(); + public InspectExecCmd withExecId(String execId); + + /** + * @throws NotFoundException if no such exec has been found + */ + @Override + public InspectExecResponse exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec {} +} diff --git a/src/main/java/com/github/dockerjava/api/command/InspectExecResponse.java b/src/main/java/com/github/dockerjava/api/command/InspectExecResponse.java new file mode 100644 index 00000000..1532f7d5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/InspectExecResponse.java @@ -0,0 +1,100 @@ +package com.github.dockerjava.api.command; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang.builder.ToStringBuilder; + +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class InspectExecResponse { + @JsonProperty("ID") + private String id; + + @JsonProperty("OpenStdin") + private boolean openStdin; + + @JsonProperty("OpenStderr") + private boolean openStderr; + + @JsonProperty("OpenStdout") + private boolean openStdout; + + @JsonProperty("Running") + private boolean running; + + @JsonProperty("ExitCode") + private int exitCode; + + public String getId() { + return id; + } + + public boolean isOpenStdin() { + return openStdin; + } + + public boolean isOpenStderr() { + return openStderr; + } + + public boolean isOpenStdout() { + return openStdout; + } + + public boolean isRunning() { + return running; + } + + public int getExitCode() { + return exitCode; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public class ProcessConfig { + @JsonProperty("arguments") + private List arguments; + + @JsonProperty("entrypoint") + private String entryPoint; + + @JsonProperty("privileged") + private boolean privileged; + + @JsonProperty("tty") + private boolean tty; + + @JsonProperty("user") + private String user; + + public List getArguments() { + return arguments; + } + + public String getEntryPoint() { + return entryPoint; + } + + public boolean isPrivileged() { + return privileged; + } + + public boolean isTty() { + return tty; + } + + public String getUser() { + return user; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } +} diff --git a/src/main/java/com/github/dockerjava/api/command/InspectImageCmd.java b/src/main/java/com/github/dockerjava/api/command/InspectImageCmd.java new file mode 100644 index 00000000..d064badb --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/InspectImageCmd.java @@ -0,0 +1,23 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +/** + * Inspect the details of an image. + */ +public interface InspectImageCmd extends DockerCmd{ + + public String getImageId(); + + public InspectImageCmd withImageId(String imageId); + + /** + * @throws NotFoundException No such image + */ + @Override + public InspectImageResponse exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/InspectImageResponse.java b/src/main/java/com/github/dockerjava/api/command/InspectImageResponse.java new file mode 100644 index 00000000..b5b6432d --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/InspectImageResponse.java @@ -0,0 +1,105 @@ +package com.github.dockerjava.api.command; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.dockerjava.api.model.ContainerConfig; + +import org.apache.commons.lang.builder.ToStringBuilder; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class InspectImageResponse { + + @JsonProperty("Architecture") + private String arch; + + @JsonProperty("Author") + private String author; + + @JsonProperty("Comment") + private String comment; + + @JsonProperty("Config") + private ContainerConfig config; + + @JsonProperty("Container") + private String container; + + @JsonProperty("ContainerConfig") + private ContainerConfig containerConfig; + + @JsonProperty("Created") + private String created; + + @JsonProperty("DockerVersion") + private String dockerVersion; + + @JsonProperty("Id") + private String id; + + @JsonProperty("Os") + private String os; + + @JsonProperty("Parent") + private String parent; + + @JsonProperty("Size") + private long size; + + public String getId() { + return id; + } + + public String getParent() { + return parent; + } + + public String getCreated() { + return created; + } + + public String getContainer() { + return container; + } + + public ContainerConfig getContainerConfig() { + return containerConfig; + } + + public long getSize() { + return size; + } + + public String getDockerVersion() { + return dockerVersion; + } + + public ContainerConfig getConfig() { + return config; + } + + public String getArch() { + return arch; + } + + public String getComment() { + return comment; + } + + public String getAuthor() { + return author; + } + + public String getOs() { + return os; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/command/KillContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/KillContainerCmd.java new file mode 100644 index 00000000..b9522cb8 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/KillContainerCmd.java @@ -0,0 +1,27 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +/** + * Kill a running container. + */ +public interface KillContainerCmd extends DockerCmd { + + public String getContainerId(); + + public String getSignal(); + + public KillContainerCmd withContainerId(String containerId); + + public KillContainerCmd withSignal(String signal); + + /** + * @throws NotFoundException No such container + */ + @Override + public Void exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/ListContainersCmd.java b/src/main/java/com/github/dockerjava/api/command/ListContainersCmd.java new file mode 100644 index 00000000..ed457dab --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/ListContainersCmd.java @@ -0,0 +1,42 @@ +package com.github.dockerjava.api.command; + +import java.util.List; + +import com.github.dockerjava.api.model.Container; + +/** + * List containers + * + * @param showAll - true or false, Show all containers. Only running containers are shown by default. + * @param showSize - true or false, Show the containers sizes. This is false by default. + * @param limit - Show `limit` last created containers, include non-running ones. There is no limit by default. + * @param sinceId - Show only containers created since Id, include non-running ones. + * @param beforeId - Show only containers created before Id, include non-running ones. + * + */ +public interface ListContainersCmd extends DockerCmd>{ + + public int getLimit(); + + public boolean hasShowSizeEnabled(); + + public boolean hasShowAllEnabled(); + + public String getSinceId(); + + public String getBeforeId(); + + public ListContainersCmd withShowAll(boolean showAll); + + public ListContainersCmd withShowSize(boolean showSize); + + public ListContainersCmd withLimit(int limit); + + public ListContainersCmd withSince(String since); + + public ListContainersCmd withBefore(String before); + + public static interface Exec extends DockerCmdExec> { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/ListImagesCmd.java b/src/main/java/com/github/dockerjava/api/command/ListImagesCmd.java new file mode 100644 index 00000000..fad6d1da --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/ListImagesCmd.java @@ -0,0 +1,26 @@ +package com.github.dockerjava.api.command; + +import java.util.List; + +import com.github.dockerjava.api.model.Image; + +/** + * List images + * + * @param showAll - Show all images (by default filter out the intermediate images used to build) + * @param filters - a json encoded value of the filters (a map[string][]string) to process on the images list. + */ +public interface ListImagesCmd extends DockerCmd> { + + public String getFilters(); + + public boolean hasShowAllEnabled(); + + public ListImagesCmd withShowAll(boolean showAll); + + public ListImagesCmd withFilters(String filters); + + public static interface Exec extends DockerCmdExec> { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/LogContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/LogContainerCmd.java new file mode 100644 index 00000000..c966ef95 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/LogContainerCmd.java @@ -0,0 +1,88 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.NotFoundException; + +import java.io.InputStream; + +/** + * Get container logs + * + * @param followStream + * - true or false, return stream. Defaults to false. + * @param stdout + * - true or false, includes stdout log. Defaults to false. + * @param stderr + * - true or false, includes stderr log. Defaults to false. + * @param timestamps + * - true or false, if true, print timestamps for every log line. + * Defaults to false. + * @param tail + * - `all` or ``, Output specified number of lines at the end + * of logs + * + * Consider wrapping any input stream you get with a frame reader to + * make reading frame easier. + * + * @see com.github.dockerjava.core.command.FrameReader + */ +public interface LogContainerCmd extends DockerCmd { + + public String getContainerId(); + + public int getTail(); + + public boolean hasFollowStreamEnabled(); + + public boolean hasTimestampsEnabled(); + + public boolean hasStdoutEnabled(); + + public boolean hasStderrEnabled(); + + public LogContainerCmd withContainerId(String containerId); + + /** + * See {@link #withFollowStream(boolean)} + */ + public LogContainerCmd withFollowStream(); + + /** + * Following the stream means the resulting {@link InputStream} returned by + * {@link #exec()} reads infinitely. So a {@link InputStream#read()} MAY + * BLOCK FOREVER as long as no data is streamed from the docker host to + * {@link DockerClient}! + */ + public LogContainerCmd withFollowStream(boolean followStream); + + public LogContainerCmd withTimestamps(); + + public LogContainerCmd withTimestamps(boolean timestamps); + + public LogContainerCmd withStdOut(); + + public LogContainerCmd withStdOut(boolean stdout); + + public LogContainerCmd withStdErr(); + + public LogContainerCmd withStdErr(boolean stderr); + + public LogContainerCmd withTailAll(); + + public LogContainerCmd withTail(int tail); + + /** + * Its the responsibility of the caller to consume and/or close the + * {@link InputStream} to prevent connection leaks. + * + * @throws NotFoundException + * No such container + */ + @Override + public InputStream exec() throws NotFoundException; + + public static interface Exec extends + DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/PauseContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/PauseContainerCmd.java new file mode 100644 index 00000000..652d433e --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/PauseContainerCmd.java @@ -0,0 +1,26 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +/** + * Pause a container. + * + * @param containerId - Id of the container + * + */ +public interface PauseContainerCmd extends DockerCmd{ + + public String getContainerId(); + + public PauseContainerCmd withContainerId(String containerId); + + /** + * @throws NotFoundException No such container + */ + @Override + public Void exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/PingCmd.java b/src/main/java/com/github/dockerjava/api/command/PingCmd.java new file mode 100644 index 00000000..7d5af1e3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/PingCmd.java @@ -0,0 +1,13 @@ +package com.github.dockerjava.api.command; + + +/** + * Ping the Docker server + * + */ +public interface PingCmd extends DockerCmd { + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java b/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java new file mode 100644 index 00000000..4b5fc4c3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java @@ -0,0 +1,43 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.core.command.EventStreamReader; + +import java.io.InputStream; + +/** +* +* Pull image from repository. +* +*/ +public interface PullImageCmd extends DockerCmd{ + + public String getRepository(); + + public String getTag(); + + public String getRegistry(); + + public AuthConfig getAuthConfig(); + + public PullImageCmd withRepository(String repository); + + public PullImageCmd withTag(String tag); + + public PullImageCmd withRegistry(String registry); + + public PullImageCmd withAuthConfig(AuthConfig authConfig); + + public static interface Exec extends DockerCmdExec { + } + + /** + * Its the responsibility of the caller to consume and/or close the {@link InputStream} to prevent + * connection leaks. + * + * @see {@link EventStreamReader} + */ + @Override + public InputStream exec(); + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/PushImageCmd.java b/src/main/java/com/github/dockerjava/api/command/PushImageCmd.java new file mode 100644 index 00000000..9ed38beb --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/PushImageCmd.java @@ -0,0 +1,51 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.PushEventStreamItem; +import com.github.dockerjava.core.command.EventStreamReader; + +import java.io.IOException; +import java.io.InputStream; + +/** + * 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 interface PushImageCmd extends DockerCmd{ + + public String getName(); + + public String getTag(); + + /** + * @param name The name, e.g. "alexec/busybox" or just "busybox" if you want to default. Not null. + */ + public PushImageCmd withName(String name); + + /** + * @param tag The image's tag. Not null. + */ + public PushImageCmd withTag(String tag); + + public AuthConfig getAuthConfig(); + + public PushImageCmd withAuthConfig(AuthConfig authConfig); + + /** + * @throws NotFoundException No such image + */ + public Response exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + + /** + * @see {@link EventStreamReader} + */ + public static abstract class Response extends InputStream { + public abstract Iterable getItems() throws IOException; + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/RemoveContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/RemoveContainerCmd.java new file mode 100644 index 00000000..b9c1f943 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/RemoveContainerCmd.java @@ -0,0 +1,36 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +/** + * Remove a container. + * + * @param removeVolumes - true or false, Remove the volumes associated to the container. Defaults to false + * @param force - true or false, Removes the container even if it was running. Defaults to false + */ +public interface RemoveContainerCmd extends DockerCmd { + + public String getContainerId(); + + public boolean hasRemoveVolumesEnabled(); + + public boolean hasForceEnabled(); + + public RemoveContainerCmd withContainerId(String containerId); + + public RemoveContainerCmd withRemoveVolumes(boolean removeVolumes); + + public RemoveContainerCmd withForce(); + + public RemoveContainerCmd withForce(boolean force); + + /** + * @throws NotFoundException No such container + */ + @Override + public Void exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/RemoveImageCmd.java b/src/main/java/com/github/dockerjava/api/command/RemoveImageCmd.java new file mode 100644 index 00000000..be65ca41 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/RemoveImageCmd.java @@ -0,0 +1,50 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +/** +* +* Remove an image, deleting any tags it might have. +* +*/ +public interface RemoveImageCmd extends DockerCmd{ + + public String getImageId(); + + public boolean hasForceEnabled(); + + public boolean hasNoPruneEnabled(); + + public RemoveImageCmd withImageId(String imageId); + + /** + * force delete of an image, even if it's tagged in multiple repositories + */ + public RemoveImageCmd withForce(); + + /** + * force parameter to force delete of an image, even if it's tagged in multiple repositories + */ + public RemoveImageCmd withForce(boolean force); + + /** + * prevent the deletion of parent images + */ + public RemoveImageCmd withNoPrune(); + + /** + * noprune parameter to prevent the deletion of parent images + * + */ + public RemoveImageCmd withNoPrune(boolean noPrune); + + /** + * @throws NotFoundException No such image + */ + @Override + public Void exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/RestartContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/RestartContainerCmd.java new file mode 100644 index 00000000..8da41d38 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/RestartContainerCmd.java @@ -0,0 +1,30 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +/** + * Restart a running container. + * + * @param timeout - Timeout in seconds before killing the container. Defaults to 10 seconds. + * + */ +public interface RestartContainerCmd extends DockerCmd { + + public String getContainerId(); + + public int getTimeout(); + + public RestartContainerCmd withContainerId(String containerId); + + public RestartContainerCmd withtTimeout(int timeout); + + /** + * @throws NotFoundException No such container + */ + @Override + public Void exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/SaveImageCmd.java b/src/main/java/com/github/dockerjava/api/command/SaveImageCmd.java new file mode 100644 index 00000000..48c5f319 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/SaveImageCmd.java @@ -0,0 +1,35 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +import java.io.InputStream; + +public interface SaveImageCmd extends DockerCmd{ + + public String getName(); + + public String getTag(); + + /** + * @param name The name, e.g. "alexec/busybox" or just "busybox" if you want to default. Not null. + */ + public SaveImageCmd withName(String name); + + /** + * @param tag The image's tag. Not null. + */ + public SaveImageCmd withTag(String tag); + + /** + * Its the responsibility of the caller to consume and/or close the {@link InputStream} to prevent + * connection leaks. + * + * @throws com.github.dockerjava.api.NotFoundException No such image + */ + public InputStream exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + + +} diff --git a/src/main/java/com/github/dockerjava/api/command/SearchImagesCmd.java b/src/main/java/com/github/dockerjava/api/command/SearchImagesCmd.java new file mode 100644 index 00000000..c609e6e8 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/SearchImagesCmd.java @@ -0,0 +1,22 @@ +package com.github.dockerjava.api.command; + +import java.util.List; + +import com.github.dockerjava.api.model.SearchItem; + +/** + * Search images + * + * @param term - search term + * + */ +public interface SearchImagesCmd extends DockerCmd> { + + public String getTerm(); + + public SearchImagesCmd withTerm(String term); + + public static interface Exec extends DockerCmdExec> { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/StartContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/StartContainerCmd.java new file mode 100644 index 00000000..e76598e3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/StartContainerCmd.java @@ -0,0 +1,170 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.NotModifiedException; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Capability; +import com.github.dockerjava.api.model.Device; +import com.github.dockerjava.api.model.Link; +import com.github.dockerjava.api.model.LxcConf; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.api.model.RestartPolicy; + +/** + * Start a container. + * + * TODO: Almost all methods are deprecated as they have corresponding siblings in {@link CreateContainerCmd} now. + */ +public interface StartContainerCmd extends DockerCmd { + + public static interface Exec extends DockerCmdExec { + } + + /** + * @throws NotFoundException + * No such container + * @throws NotModifiedException + * Container already started + */ + @Override + public Void exec() throws NotFoundException, NotModifiedException; + + public Bind[] getBinds(); + + public Capability[] getCapAdd(); + + public Capability[] getCapDrop(); + + public String getContainerId(); + + public Device[] getDevices(); + + public String[] getDns(); + + public String[] getDnsSearch(); + + public String[] getExtraHosts(); + + public Link[] getLinks(); + + public LxcConf[] getLxcConf(); + + public String getNetworkMode(); + + public Ports getPortBindings(); + + public RestartPolicy getRestartPolicy(); + + public String getVolumesFrom(); + + public Boolean isPrivileged(); + + public Boolean isPublishAllPorts(); + + @Deprecated + public StartContainerCmd withBinds(Bind... binds); + + /** + * Add linux kernel + * capability to the container. For example: adding {@link Capability#MKNOD} + * allows the container to create special files using the 'mknod' command. + */ + @Deprecated + public StartContainerCmd withCapAdd(Capability... capAdd); + + /** + * Drop linux kernel + * capability from the container. For example: dropping {@link Capability#CHOWN} + * prevents the container from changing the owner of any files. + */ + @Deprecated + public StartContainerCmd withCapDrop(Capability... capDrop); + + @Deprecated + public StartContainerCmd withContainerId(String containerId); + + /** + * Add host devices to the container + */ + @Deprecated + public StartContainerCmd withDevices(Device... devices); + + /** + * Set custom DNS servers + */ + @Deprecated + public StartContainerCmd withDns(String... dns); + + /** + * Set custom DNS search domains + */ + @Deprecated + public StartContainerCmd withDnsSearch(String... dnsSearch); + + /** + * Add hostnames to /etc/hosts in the container + */ + @Deprecated + public StartContainerCmd withExtraHosts(String... extraHosts); + + /** + * Add link to another container. + */ + @Deprecated + public StartContainerCmd withLinks(Link... links); + + @Deprecated + public StartContainerCmd withLxcConf(LxcConf... lxcConf); + + /** + * Set the Network mode for the container + *
    + *
  • 'bridge': creates a new network stack for the container on the docker + * bridge
  • + *
  • 'none': no networking for this container
  • + *
  • 'container:': reuses another container network stack
  • + *
  • 'host': use the host network stack inside the container. Note: the + * host mode gives the container full access to local system services such + * as D-bus and is therefore considered insecure.
  • + *
+ */ + @Deprecated + public StartContainerCmd withNetworkMode(String networkMode); + + /** + * Add one or more {@link PortBinding}s. + * This corresponds to the --publish (-p) + * option of the docker run CLI command. + */ + @Deprecated + public StartContainerCmd withPortBindings(PortBinding... portBindings); + + /** + * Add the port bindings that are contained in the given {@link Ports} + * object. + * + * @see #withPortBindings(PortBinding...) + */ + @Deprecated + public StartContainerCmd withPortBindings(Ports portBindings); + + @Deprecated + public StartContainerCmd withPrivileged(Boolean privileged); + + @Deprecated + public StartContainerCmd withPublishAllPorts(Boolean publishAllPorts); + + /** + * Set custom {@link RestartPolicy} for the container. Defaults to + * {@link RestartPolicy#noRestart()} + */ + @Deprecated + public StartContainerCmd withRestartPolicy(RestartPolicy restartPolicy); + + @Deprecated + public StartContainerCmd withVolumesFrom(String volumesFrom); + +} diff --git a/src/main/java/com/github/dockerjava/api/command/StatsCmd.java b/src/main/java/com/github/dockerjava/api/command/StatsCmd.java new file mode 100644 index 00000000..dc57196e --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/StatsCmd.java @@ -0,0 +1,15 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.model.Stats; + +/** + * @author Heng WU(wuheng09@otcaix.iscas.ac.cn) + * + */ +public interface StatsCmd extends DockerCmd { + + public static interface Exec extends DockerCmdExec { + } + + public String getContainerId(); +} diff --git a/src/main/java/com/github/dockerjava/api/command/StopContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/StopContainerCmd.java new file mode 100644 index 00000000..19c24855 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/StopContainerCmd.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.NotModifiedException; + +/** + * Stop a running container. + * + * @param containerId - Id of the container + * @param timeout - Timeout in seconds before killing the container. Defaults to 10 seconds. + * + */ +public interface StopContainerCmd extends DockerCmd { + + public String getContainerId(); + + public int getTimeout(); + + public StopContainerCmd withContainerId(String containerId); + + public StopContainerCmd withTimeout(int timeout); + + /** + * @throws NotFoundException No such container + * @throws NotModifiedException Container already stopped + */ + @Override + public Void exec() throws NotFoundException, NotModifiedException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/TagImageCmd.java b/src/main/java/com/github/dockerjava/api/command/TagImageCmd.java new file mode 100644 index 00000000..ace84996 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/TagImageCmd.java @@ -0,0 +1,35 @@ +package com.github.dockerjava.api.command; + + +/** + * 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 force (not documented) + * + */ +public interface TagImageCmd extends DockerCmd { + + public String getImageId(); + + public String getRepository(); + + public String getTag(); + + public boolean hasForceEnabled(); + + public TagImageCmd withImageId(String imageId); + + public TagImageCmd withRepository(String repository); + + public TagImageCmd withTag(String tag); + + public TagImageCmd withForce(); + + public TagImageCmd withForce(boolean force); + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/TopContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/TopContainerCmd.java new file mode 100644 index 00000000..63865c2a --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/TopContainerCmd.java @@ -0,0 +1,27 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +/** + * List processes running inside a container + */ +public interface TopContainerCmd extends DockerCmd { + + public String getContainerId(); + + public String getPsArgs(); + + public TopContainerCmd withContainerId(String containerId); + + public TopContainerCmd withPsArgs(String psArgs); + + /** + * @throws NotFoundException No such container + */ + @Override + public TopContainerResponse exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/TopContainerResponse.java b/src/main/java/com/github/dockerjava/api/command/TopContainerResponse.java new file mode 100644 index 00000000..c92ffa4c --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/TopContainerResponse.java @@ -0,0 +1,43 @@ +package com.github.dockerjava.api.command; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Joiner; + +/** + * + * @author marcus + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class TopContainerResponse { + + @JsonProperty("Titles") + private String[] titles; + + @JsonProperty("Processes") + private String[][] processes; + + public String[] getTitles() { + return titles; + } + + public String[][] getProcesses() { + return processes; + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("["); + for(String[] fields: processes) { + buffer.append("[" + Joiner.on("; ").skipNulls().join(fields) + "]"); + } + buffer.append("]"); + + return "TopContainerResponse{" + + "titles=" + Joiner.on("; ").skipNulls().join(titles) + + ", processes=" + buffer.toString() + + '}'; + } +} diff --git a/src/main/java/com/github/dockerjava/api/command/UnpauseContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/UnpauseContainerCmd.java new file mode 100644 index 00000000..166bfa6a --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/UnpauseContainerCmd.java @@ -0,0 +1,26 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +/** + * Unpause a container. + * + * @param containerId - Id of the container + * + */ +public interface UnpauseContainerCmd extends DockerCmd { + + public String getContainerId(); + + public UnpauseContainerCmd withContainerId(String containerId); + + /** + * @throws NotFoundException No such container + */ + @Override + public Void exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/VersionCmd.java b/src/main/java/com/github/dockerjava/api/command/VersionCmd.java new file mode 100644 index 00000000..740a335e --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/VersionCmd.java @@ -0,0 +1,13 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.model.Version; + +/** + * Returns the Docker version info. + */ +public interface VersionCmd extends DockerCmd { + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/command/WaitContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/WaitContainerCmd.java new file mode 100644 index 00000000..e7183155 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/WaitContainerCmd.java @@ -0,0 +1,25 @@ +package com.github.dockerjava.api.command; + +import com.github.dockerjava.api.NotFoundException; + +/** + * Wait a container + * + * Block until container stops, then returns its exit code + */ +public interface WaitContainerCmd extends DockerCmd { + + public String getContainerId(); + + public WaitContainerCmd withContainerId(String containerId); + + /** + * @throws NotFoundException container not found + */ + @Override + public Integer exec() throws NotFoundException; + + public static interface Exec extends DockerCmdExec { + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/api/model/AccessMode.java b/src/main/java/com/github/dockerjava/api/model/AccessMode.java new file mode 100644 index 00000000..a893e7f3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/AccessMode.java @@ -0,0 +1,28 @@ +package com.github.dockerjava.api.model; + +/** + * The access mode of a file system or file: read-write + * or read-only. + */ +public enum AccessMode { + /** read-write */ + rw, + + /** read-only */ + ro; + + /** + * The default {@link AccessMode}: {@link #rw} + */ + public static final AccessMode DEFAULT = rw; + + public static final AccessMode fromBoolean(boolean accessMode) { + return accessMode ? rw : ro; + } + + public final boolean toBoolean() { + return this.equals(AccessMode.rw) ? true: false; + } + + +} diff --git a/src/main/java/com/github/dockerjava/api/model/AuthConfig.java b/src/main/java/com/github/dockerjava/api/model/AuthConfig.java new file mode 100644 index 00000000..42c49bce --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/AuthConfig.java @@ -0,0 +1,129 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class AuthConfig { + + /** + * For backwards compatibility. Make sure you update the properties if you change this. + * + * @see "/docker.io.properties" + */ + public static final String DEFAULT_SERVER_ADDRESS = "https://index.docker.io/v1/"; + + @JsonProperty + private String username; + + @JsonProperty + private String password; + + @JsonProperty + private String email; + + @JsonProperty("serveraddress") + private String serverAddress = DEFAULT_SERVER_ADDRESS; + + private String auth; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getServerAddress() { + return serverAddress; + } + + public void setServerAddress(String serverAddress) { + this.serverAddress = serverAddress; + } + + @JsonIgnore + public String getAuth() { + return auth; + } + + @JsonProperty("auth") + public void setAuth(String auth) { + this.auth = auth; + } + + @Override + public String toString() { + return "AuthConfig{" + + "username='" + username + '\'' + + ", password='" + password + '\'' + + ", email='" + email + '\'' + + ", serverAddress='" + serverAddress + '\'' + + '}'; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((auth == null) ? 0 : auth.hashCode()); + result = prime * result + ((email == null) ? 0 : email.hashCode()); + result = prime * result + ((password == null) ? 0 : password.hashCode()); + result = prime * result + ((serverAddress == null) ? 0 : serverAddress.hashCode()); + result = prime * result + ((username == null) ? 0 : username.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AuthConfig other = (AuthConfig) obj; + if (auth == null) { + if (other.auth != null) + return false; + } else if (!auth.equals(other.auth)) + return false; + if (email == null) { + if (other.email != null) + return false; + } else if (!email.equals(other.email)) + return false; + if (password == null) { + if (other.password != null) + return false; + } else if (!password.equals(other.password)) + return false; + if (serverAddress == null) { + if (other.serverAddress != null) + return false; + } else if (!serverAddress.equals(other.serverAddress)) + return false; + if (username == null) { + if (other.username != null) + return false; + } else if (!username.equals(other.username)) + return false; + return true; + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/AuthConfigurations.java b/src/main/java/com/github/dockerjava/api/model/AuthConfigurations.java new file mode 100644 index 00000000..5a62c758 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/AuthConfigurations.java @@ -0,0 +1,21 @@ +package com.github.dockerjava.api.model; + +import java.util.Map; +import java.util.TreeMap; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class AuthConfigurations { + + @JsonProperty("configs") + private Map configs = new TreeMap<>(); + + public void addConfig(AuthConfig authConfig){ + configs.put(authConfig.getServerAddress(), authConfig); + } + + public Map getConfigs(){ + return this.configs; + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/AuthResponse.java b/src/main/java/com/github/dockerjava/api/model/AuthResponse.java new file mode 100644 index 00000000..cb5d9df8 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/AuthResponse.java @@ -0,0 +1,12 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class AuthResponse { + @JsonProperty("Status") + private String status; + + public String getStatus() { + return status; + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Bind.java b/src/main/java/com/github/dockerjava/api/model/Bind.java new file mode 100644 index 00000000..0071bff9 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Bind.java @@ -0,0 +1,102 @@ +package com.github.dockerjava.api.model; + + + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +/** + * Represents a host path being bind mounted as a {@link Volume} + * in a Docker container. + * The Bind can be in read only or read write access mode. + */ +public class Bind { + + private String path; + + private Volume volume; + + private AccessMode accessMode; + + public Bind(String path, Volume volume) { + this(path, volume, AccessMode.DEFAULT); + } + + public Bind(String path, Volume volume, AccessMode accessMode) { + this.path = path; + this.volume = volume; + this.accessMode = accessMode; + } + + public String getPath() { + return path; + } + + public Volume getVolume() { + return volume; + } + + public AccessMode getAccessMode() { + return accessMode; + } + + + /** + * Parses a bind mount specification to a {@link Bind}. + * + * @param serialized the specification, e.g. /host:/container:ro + * @return a {@link Bind} matching the specification + * @throws IllegalArgumentException if the specification cannot be parsed + */ + public static Bind parse(String serialized) { + try { + String[] parts = serialized.split(":"); + switch (parts.length) { + case 2: { + return new Bind(parts[0], new Volume(parts[1])); + } + case 3: { + AccessMode accessMode = AccessMode.valueOf(parts[2].toLowerCase()); + return new Bind(parts[0], new Volume(parts[1]), accessMode); + } + default: { + throw new IllegalArgumentException(); + } + } + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing Bind '" + serialized + + "'"); + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Bind) { + Bind other = (Bind) obj; + return new EqualsBuilder().append(path, other.getPath()) + .append(volume, other.getVolume()) + .append(accessMode, other.getAccessMode()).isEquals(); + } else + return super.equals(obj); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(path).append(volume) + .append(accessMode).toHashCode(); + } + + /** + * Returns a string representation of this {@link Bind} suitable + * for inclusion in a JSON message. + * The format is <host path>:<container path>:<access mode>, + * like the argument in {@link #parse(String)}. + * + * @return a string representation of this {@link Bind} + */ + @Override + public String toString() { + return path + ":" + volume.getPath() + ":" + accessMode.toString(); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/Binds.java b/src/main/java/com/github/dockerjava/api/model/Binds.java new file mode 100644 index 00000000..bfc8dbf2 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Binds.java @@ -0,0 +1,73 @@ +package com.github.dockerjava.api.model; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.NullNode; + + +@JsonSerialize(using = Binds.Serializer.class) +@JsonDeserialize(using = Binds.Deserializer.class) +public class Binds { + + private Bind[] binds; + + public Binds(Bind... binds) { + this.binds = binds; + } + + public Bind[] getBinds() { + return binds; + } + + public static class Serializer extends JsonSerializer { + + @Override + public void serialize(Binds binds, JsonGenerator jsonGen, + SerializerProvider serProvider) throws IOException, + JsonProcessingException { + + // + jsonGen.writeStartArray(); + for (Bind bind : binds.getBinds()) { + jsonGen.writeString(bind.toString()); + } + jsonGen.writeEndArray(); + // + } + + } + + public static class Deserializer extends JsonDeserializer { + @Override + public Binds deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + + List binds = new ArrayList(); + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + for (Iterator> it = node.fields(); it.hasNext();) { + + Map.Entry field = it.next(); + if (!field.getValue().equals(NullNode.getInstance())) { + binds.add(Bind.parse(field.getKey())); + } + } + return new Binds(binds.toArray(new Bind[0])); + } + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/Capability.java b/src/main/java/com/github/dockerjava/api/model/Capability.java new file mode 100644 index 00000000..c86c0796 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Capability.java @@ -0,0 +1,329 @@ +package com.github.dockerjava.api.model; + +/** + * The Linux capabilities supported by Docker. + * The list of capabilities is defined in Docker's types.go, + * {@link #ALL} was added manually. + * + * @see http://man7.org/linux/man-pages/man7/capabilities.7.html + */ +public enum Capability { + /** + * This meta capability includes all Linux capabilities. + */ + ALL, + /** + *
    + *
  • Enable and disable kernel auditing. + *
  • Change auditing filter rules. + *
  • Retrieve auditing status and filtering rules. + *
+ */ + AUDIT_CONTROL, + /** + * Write records to kernel auditing log. + */ + AUDIT_WRITE, + /** + * Employ features that can block system suspend. + */ + BLOCK_SUSPEND, + /** + * Make arbitrary changes to file UIDs and GIDs (see chown(2)). + */ + CHOWN, + /** + * Bypass file read, write, and execute permission checks. + * (DAC is an abbreviation of "discretionary access control".) + */ + DAC_OVERRIDE, + /** + * Bypass file read permission checks and directory read and + * execute permission checks. + */ + DAC_READ_SEARCH, + /** + *
    + *
  • Bypass permission checks on operations that normally require + * the file system UID of the process to match the UID of the file + * (e.g., chmod(2), utime(2)), excluding those operations covered + * by the {@link #DAC_OVERRIDE} and{@link #DAC_READ_SEARCH}. + *
  • Set extended file attributes (see chattr(1)) on arbitrary files. + *
  • Set Access Control Lists (ACLs) on arbitrary files. + *
  • Ignore directory sticky bit on file deletion. + *
  • Specify O_NOATIME for arbitrary files in open(2)and fcntl(2). + *
+ */ + FOWNER, + /** + *
    + *
  • Don't clear set-user-ID and set-group-ID permission bits when + * a file is modified. + *
  • Set the set-group-ID bit for a file whose GID does not match + * the file system or any of the supplementary GIDs of the calling + * process. + *
+ */ + FSETID, + /** + * Permit memory locking (mlock(2), mlockall(2), mmap(2), shmctl(2)). + */ + IPC_LOCK, + /** + * Bypass permission checks for operations on System V IPC objects. + */ + IPC_OWNER, + /** + * Bypass permission checks for sending signals (see kill(2)). + * This includes use of the ioctl(2) KDSIGACCEPT operation. + */ + KILL, + /** + * Establish leases on arbitrary files (see fcntl(2)). + */ + LEASE, + /** + * Set the FS_APPEND_FL and FS_IMMUTABLE_FL i-node flags (see chattr(1)). + */ + LINUX_IMMUTABLE, + /** + * Override Mandatory Access Control (MAC). + * Implemented for the Smack Linux Security Module (LSM). + */ + MAC_ADMIN, + /** + * Allow MAC configuration or state changes. Implemented for the Smack LSM. + */ + MAC_OVERRIDE, + /** + * Create special files using mknod(2). + */ + MKNOD, + /** + * Perform various network-related operations: + *
    + *
  • Interface configuration. + *
  • Administration of IP firewall, masquerading, and accounting. + *
  • Modify routing tables. + *
  • Bind to any address for transparent proxying. + *
  • Set type-of-service (TOS). + *
  • Clear driver statistics. + *
  • Set promiscuous mode. + *
  • Enabling multicasting. + *
  • Use setsockopt(2) to set the following socket options: SO_DEBUG, + * SO_MARK, SO_PRIORITY (for a priority outside the range 0 to 6), + * SO_RCVBUFFORCE, and SO_SNDBUFFORCE. + *
+ */ + NET_ADMIN, + /** + * Bind a socket to Internet domain privileged ports (port numbers less + * than 1024). + */ + NET_BIND_SERVICE, + /** + * (Unused) Make socket broadcasts, and listen to multicasts. + */ + NET_BROADCAST, + /** + *
    + *
  • Use RAW and PACKET sockets. + *
  • Bind to any address for transparent proxying. + *
+ */ + NET_RAW, + /** + * Set file capabilities. + */ + SETFCAP, + /** + *
    + *
  • Make arbitrary manipulations of process GIDs and supplementary + * GID list. + *
  • Forge GID when passing socket credentials via UNIX domain + * sockets. + *
+ */ + SETGID, + /** + * If file capabilities are not supported: + *
    + *
  • grant or remove any capability in the caller's permitted + * capability set to or from any other process. (This property of + * CAP_SETPCAP is not available when the kernel is configured to + * support file capabilities, since CAP_SETPCAP has entirely different + * semantics for such kernels.) + *
+ *

+ * If file capabilities are supported: + *

    + *
  • Add any capability from the calling thread's bounding set to its + * inheritable set. + *
  • Drop capabilities from the bounding set (via prctl(2) + * PR_CAPBSET_DROP). + *
  • Make changes to the securebits flags. + *
+ */ + SETPCAP, + /** + *
    + *
  • Make arbitrary manipulations of process UIDs (setuid(2), + * setreuid(2), setresuid(2), setfsuid(2)). + *
  • Make forged UID when passing socket credentials via UNIX domain + * sockets. + *
+ */ + SETUID, + /** + *
    + *
  • Perform a range of system administration operations including: + * quotactl(2), mount(2), umount(2), swapon(2), swapoff(2), sethostname(2), + * and setdomainname(2). + *
  • Perform privileged syslog(2) operations (since Linux 2.6.37, + * CAP_SYSLOG should be used to permit such operations). + *
  • Perform VM86_REQUEST_IRQ vm86(2) command. + *
  • Perform IPC_SET and IPC_RMID operations on arbitrary System V IPC objects. + *
  • Perform operations on trusted and security Extended Attributes + * (see attr(5)). + *
  • Use lookup_dcookie(2) + *
  • Use ioprio_set(2) to assign IOPRIO_CLASS_RT and (before Linux 2.6.25) + * IOPRIO_CLASS_IDLE I/O scheduling classes. + *
  • Forge UID when passing socket credentials. + *
  • Exceed /proc/sys/fs/file-max, the system-wide limit on the number of + * open files, in system calls that open files (e.g., accept(2), execve(2), + * open(2), pipe(2)). + *
  • Employ CLONE_* flags that create new namespaces with clone(2) and + * unshare(2). + *
  • Call perf_event_open(2). + *
  • Access privileged perf event information. + *
  • Call setns(2). + *
  • Call fanotify_init(2). + *
  • Perform KEYCTL_CHOWN and KEYCTL_SETPERM keyctl(2) operations. + *
  • Perform madvise(2) MADV_HWPOISON operation. + *
  • Employ the TIOCSTI ioctl(2) to insert characters into the input queue + * of a terminal other than the caller's controlling terminal. + *
  • Employ the obsolete nfsservctl(2) system call. + *
  • Employ the obsolete bdflush(2) system call. + *
  • Perform various privileged block-device ioctl(2) operations. + *
  • Perform various privileged file-system ioctl(2) operations. + *
  • Perform administrative operations on many device drivers. + *
+ */ + SYS_ADMIN, + /** + * Use reboot(2) and kexec_load(2). + */ + SYS_BOOT, + /** + * Use chroot(2). + */ + SYS_CHROOT, + /** + *
    + *
  • Perform privileged syslog(2) operations. See syslog(2) for information + * on which operations require privilege. + *
  • View kernel addresses exposed via /proc and other interfaces when + * /proc/sys/kernel/kptr_restrict has the value 1. (See the discussion of the + * kptr_restrict in proc(5).) + *
+ */ + SYSLOG, + /** + *
    + *
  • Load and unload kernel modules (see init_module(2) and delete_module(2)) + *
  • In kernels before 2.6.25: drop capabilities from the system-wide + * capability bounding set. + *
+ */ + SYS_MODULE, + /** + *
    + *
  • Raise process nice value (nice(2), setpriority(2)) and change the nice + * value for arbitrary processes. + *
  • Set real-time scheduling policies for calling process, and set scheduling + * policies and priorities for arbitrary processes (sched_setscheduler(2), + * sched_setparam(2)). + *
  • Set CPU affinity for arbitrary processes (sched_setaffinity(2)). + *
  • Set I/O scheduling class and priority for arbitrary processes + * (ioprio_set(2)). + *
  • Apply migrate_pages(2) to arbitrary processes and allow processes to be + * migrated to arbitrary nodes. + *
  • Apply move_pages(2) to arbitrary processes. + *
  • Use the MPOL_MF_MOVE_ALL flag with mbind(2) and move_pages(2). + *
+ */ + SYS_NICE, + /** + * Use acct(2). + */ + SYS_PACCT, + /** + *
    + *
  • Trace arbitrary processes using ptrace(2). + *
  • Apply get_robust_list(2) to arbitrary processes. + *
  • Inspect processes using kcmp(2). + *
+ */ + SYS_PTRACE, + /** + *
    + *
  • Perform I/O port operations (iopl(2) and ioperm(2)). + *
  • Access /proc/kcore. + *
  • Employ the FIBMAP ioctl(2) operation. + *
  • Open devices for accessing x86 model-specific registers (MSRs, see + * msr(4)). + *
  • Update /proc/sys/vm/mmap_min_addr. + *
  • Create memory mappings at addresses below the value specified by + * /proc/sys/vm/mmap_min_addr. + *
  • Map files in /proc/pci/bus. + *
  • Open /dev/mem and /dev/kmem. + *
  • Perform various SCSI device commands. + *
  • Perform certain operations on hpsa(4) and cciss(4) devices. + *
  • Perform a range of device-specific operations on other devices. + *
+ */ + SYS_RAWIO, + /** + *
    + *
  • Use reserved space on ext2 file systems. + *
  • Make ioctl(2) calls controlling ext3 journaling. + *
  • Override disk quota limits. + *
  • Increase resource limits (see setrlimit(2)). + *
  • Override RLIMIT_NPROC resource limit. + *
  • Override maximum number of consoles on console allocation. + *
  • Override maximum number of keymaps. + *
  • Allow more than 64hz interrupts from the real-time clock. + *
  • Raise msg_qbytes limit for a System V message queue above the limit + * in /proc/sys/kernel/msgmnb (see msgop(2) and msgctl(2)). + *
  • Override the /proc/sys/fs/pipe-size-max limit when setting the capacity + * of a pipe using the F_SETPIPE_SZ fcntl(2) command. + *
  • Use F_SETPIPE_SZ to increase the capacity of a pipe above the limit + * specified by /proc/sys/fs/pipe-max-size. + *
  • Override /proc/sys/fs/mqueue/queues_max limit when creating POSIX + * message queues (see mq_overview(7)). + *
  • Employ prctl(2) PR_SET_MM operation. + *
  • Set /proc/PID/oom_score_adj to a value lower than the value last set + * by a process with CAP_SYS_RESOURCE. + *
+ */ + SYS_RESOURCE, + /** + *
    + *
  • Set system clock (settimeofday(2), stime(2), adjtimex(2)). + *
  • Set real-time (hardware) clock. + *
+ */ + SYS_TIME, + /** + *
    + *
  • Use vhangup(2). + *
  • Employ various privileged ioctl(2) operations on virtual terminals. + *
+ */ + SYS_TTY_CONFIG, + /** + * Trigger something that will wake up the system (set CLOCK_REALTIME_ALARM and + * CLOCK_BOOTTIME_ALARM timers). + */ + WAKE_ALARM +} diff --git a/src/main/java/com/kpelykh/docker/client/model/ChangeLog.java b/src/main/java/com/github/dockerjava/api/model/ChangeLog.java similarity index 71% rename from src/main/java/com/kpelykh/docker/client/model/ChangeLog.java rename to src/main/java/com/github/dockerjava/api/model/ChangeLog.java index 50fdab4a..9bc91069 100644 --- a/src/main/java/com/kpelykh/docker/client/model/ChangeLog.java +++ b/src/main/java/com/github/dockerjava/api/model/ChangeLog.java @@ -1,35 +1,33 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ChangeLog { - - @JsonProperty("Path") - private String path; - - @JsonProperty("Kind") - private int kind; - - public String getPath() { - return path; - } - - public int getKind() { - return kind; - } - - @Override - public String toString() { - return "ChangeLog{" + - "path='" + path + '\'' + - ", kind=" + kind + - '}'; - } -} +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang.builder.ToStringBuilder; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ChangeLog { + + @JsonProperty("Path") + private String path; + + @JsonProperty("Kind") + private int kind; + + public String getPath() { + return path; + } + + public int getKind() { + return kind; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Container.java b/src/main/java/com/github/dockerjava/api/model/Container.java new file mode 100644 index 00000000..5b38ce48 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Container.java @@ -0,0 +1,107 @@ +package com.github.dockerjava.api.model; + +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +@JsonIgnoreProperties(ignoreUnknown=true) +public class Container { + + @JsonProperty("Command") + private String command; + + @JsonProperty("Created") + private long created; + + @JsonProperty("Id") + private String id; + + @JsonProperty("Image") + private String image; + + @JsonProperty("Names") + private String[] names; + + @JsonProperty("Ports") + public Port[] ports; + + @JsonProperty("Status") + private String status; + + public String getId() { + return id; + } + + public String getCommand() { + return command; + } + + public String getImage() { + return image; + } + + public long getCreated() { + return created; + } + + public String getStatus() { + return status; + } + + public Port[] getPorts() { + return ports; + } + + public String[] getNames() { + return names; + } + + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Port { + + @JsonProperty("IP") + private String ip; + + @JsonProperty("PrivatePort") + private Integer privatePort; + + @JsonProperty("PublicPort") + private Integer publicPort; + + @JsonProperty("Type") + private String type; + + public String getIp() { + return ip; + } + + public Integer getPrivatePort() { + return privatePort; + } + + public Integer getPublicPort() { + return publicPort; + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java b/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java new file mode 100644 index 00000000..ce8c6b88 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java @@ -0,0 +1,191 @@ +package com.github.dockerjava.api.model; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang.builder.ToStringBuilder; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ContainerConfig { + + @JsonProperty("AttachStderr") + private boolean attachStderr = false; + + @JsonProperty("AttachStdin") + private boolean attachStdin = false; + + @JsonProperty("AttachStdout") + private boolean attachStdout = false; + + @JsonProperty("Cmd") + private String[] cmd; + + @JsonProperty("CpuShares") + private int cpuShares = 0; + + @JsonProperty("Cpuset") + private String cpuset = ""; + + @JsonProperty("Domainname") + private String domainName = ""; + + @JsonProperty("Entrypoint") + private String[] entrypoint = new String[] {}; + + @JsonProperty("Env") + private String[] env; + + @JsonProperty("ExposedPorts") + private ExposedPorts exposedPorts; + + @JsonProperty("Hostname") + private String hostName = ""; + + @JsonProperty("Image") + private String image; + + @JsonProperty("MacAddress") + private String macAddress; + + @JsonProperty("Memory") + private long memoryLimit = 0; + + @JsonProperty("MemorySwap") + private long memorySwap = 0; + + @JsonProperty("NetworkDisabled") + private boolean networkDisabled = false; + + @JsonProperty("OnBuild") + private String[] onBuild; + + @JsonProperty("OpenStdin") + private boolean stdinOpen = false; + + @JsonProperty("PortSpecs") + private String[] portSpecs; + + @JsonProperty("StdinOnce") + private boolean stdInOnce = false; + + @JsonProperty("Tty") + private boolean tty = false; + + @JsonProperty("User") + private String user = ""; + + @JsonProperty("Volumes") + private Map volumes; + + @JsonProperty("WorkingDir") + private String workingDir = ""; + + @JsonIgnore + public ExposedPort[] getExposedPorts() { + return exposedPorts.getExposedPorts(); + } + + public boolean isNetworkDisabled() { + return networkDisabled; + } + + public String getDomainName() { + return domainName; + } + + public String getWorkingDir() { + return workingDir; + } + + public String getHostName() { + return hostName; + } + + public String[] getPortSpecs() { + return portSpecs; + } + + public String getUser() { + return user; + } + + public boolean isTty() { + return tty; + } + + public boolean isStdinOpen() { + return stdinOpen; + } + + public boolean isStdInOnce() { + return stdInOnce; + } + + public String getMacAddress() { + return macAddress; + } + + public long getMemoryLimit() { + return memoryLimit; + } + + public long getMemorySwap() { + return memorySwap; + } + + public int getCpuShares() { + return cpuShares; + } + + public String getCpuset() { + return cpuset; + } + + public boolean isAttachStdin() { + return attachStdin; + } + + public boolean isAttachStdout() { + return attachStdout; + } + + public boolean isAttachStderr() { + return attachStderr; + } + + public String[] getEnv() { + return env; + } + + public String[] getCmd() { + return cmd; + } + + public String getImage() { + return image; + } + + public Map getVolumes() { + return volumes; + } + + public String[] getEntrypoint() { + return entrypoint; + } + + public String[] getOnBuild() { + return onBuild; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Device.java b/src/main/java/com/github/dockerjava/api/model/Device.java new file mode 100644 index 00000000..574d1ad5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Device.java @@ -0,0 +1,65 @@ +package com.github.dockerjava.api.model; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Device { + + @JsonProperty("CgroupPermissions") + private String cGroupPermissions = ""; + + @JsonProperty("PathOnHost") + private String pathOnHost = null; + + @JsonProperty("PathInContainer") + private String pathInContainer = null; + + public Device() { + } + + public Device(String cGroupPermissions, String pathInContainer, + String pathOnHost) { + checkNotNull(cGroupPermissions, + "cGroupPermissions is null"); + checkNotNull(pathInContainer, "pathInContainer is null"); + checkNotNull(pathOnHost, "pathOnHost is null"); + this.cGroupPermissions = cGroupPermissions; + this.pathInContainer = pathInContainer; + this.pathOnHost = pathOnHost; + } + + public String getcGroupPermissions() { + return cGroupPermissions; + } + + public String getPathInContainer() { + return pathInContainer; + } + + public String getPathOnHost() { + return pathOnHost; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Device) { + Device other = (Device) obj; + return new EqualsBuilder() + .append(cGroupPermissions, other.getcGroupPermissions()) + .append(pathInContainer, other.getPathInContainer()) + .append(pathOnHost, other.getPathOnHost()).isEquals(); + } else + return super.equals(obj); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(cGroupPermissions) + .append(pathInContainer).append(pathOnHost).toHashCode(); + } + +} diff --git a/src/main/java/com/kpelykh/docker/client/model/DriverStatus.java b/src/main/java/com/github/dockerjava/api/model/DriverStatus.java similarity index 70% rename from src/main/java/com/kpelykh/docker/client/model/DriverStatus.java rename to src/main/java/com/github/dockerjava/api/model/DriverStatus.java index 5421bc44..c1bb8b7d 100644 --- a/src/main/java/com/kpelykh/docker/client/model/DriverStatus.java +++ b/src/main/java/com/github/dockerjava/api/model/DriverStatus.java @@ -1,33 +1,31 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Created by ben on 12/12/13. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class DriverStatus { - - @JsonProperty("Root Dir") - private String rootDir; - - @JsonProperty("Dirs") - private int dirs; - - public String getRootDir() { - return rootDir; - } - - public int getDirs() { - return dirs; - } - - @Override - public String toString() { - return "DriverStatus{" + - "rootDir='" + rootDir + '\'' + - ", dirs=" + dirs + - '}'; - } -} +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang.builder.ToStringBuilder; + +/** + * Created by ben on 12/12/13. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class DriverStatus { + + @JsonProperty("Root Dir") + private String rootDir; + + @JsonProperty("Dirs") + private int dirs; + + public String getRootDir() { + return rootDir; + } + + public int getDirs() { + return dirs; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/ErrorDetail.java b/src/main/java/com/github/dockerjava/api/model/ErrorDetail.java new file mode 100644 index 00000000..8abffe6c --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/ErrorDetail.java @@ -0,0 +1,12 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ErrorDetail { + @JsonProperty + private String message; + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/ErrorResponse.java b/src/main/java/com/github/dockerjava/api/model/ErrorResponse.java new file mode 100644 index 00000000..87d9b0d7 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/ErrorResponse.java @@ -0,0 +1,18 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ErrorResponse { + @JsonProperty + private ErrorDetail errorDetail; + @JsonProperty + private String error; + + public ErrorDetail getErrorDetail() { + return errorDetail; + } + + public String getError() { + return error; + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Event.java b/src/main/java/com/github/dockerjava/api/model/Event.java new file mode 100644 index 00000000..cb961f97 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Event.java @@ -0,0 +1,78 @@ +package com.github.dockerjava.api.model; + +import org.apache.commons.lang.builder.ToStringBuilder; + +/** + * Representation of a Docker event. + */ +public class Event { + private String status; + + private String id; + + private String from; + + private long time; + + /** + * Default constructor for the deserialization. + */ + public Event() { + } + + /** + * Constructor. + * @param id Container ID + * @param status Status string. + * List of statuses is available in Docker API v.1.16 + * @param from Image, from which the container has been created + * @param time Event time + * The time is specified in milliseconds since January 1, 1970, 00:00:00 GMT + * @since TODO + */ + public Event(String status, String id, String from, long time) { + this.status = status; + this.id = id; + this.from = from; + this.time = time; + } + + /** + * Status of docker image or container. + * List of statuses is available in Docker API v.1.16 + * @return Status string + */ + public String getStatus() { + return status; + } + + /** + * Get ID of docker container. + * @return Container ID + */ + public String getId() { + return id; + } + + /** + * Get source image of the container. + * @return Name of the parent container + */ + public String getFrom() { + return from; + } + + /** + * Get the event time. + * The time is specified in milliseconds since January 1, 1970, 00:00:00 GMT + * @return Event time in the specified format. + */ + public long getTime() { + return time; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/EventStreamItem.java b/src/main/java/com/github/dockerjava/api/model/EventStreamItem.java new file mode 100644 index 00000000..010ed9af --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/EventStreamItem.java @@ -0,0 +1,66 @@ +package com.github.dockerjava.api.model; + + + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; + +import com.google.common.base.Objects; + +/** + * Represents an event stream + */ +@JsonIgnoreProperties(ignoreUnknown=true) +public class EventStreamItem implements Serializable { + + private static final long serialVersionUID = 638778515773898651L; + + @JsonProperty("stream") + private String stream; + + // {"error":"Error...", "errorDetail":{"code": 123, "message": "Error..."}} + @JsonProperty("error") + private String error; + + @JsonProperty("errorDetail") + private ErrorDetail errorDetail; + + public String getStream() { + return stream; + } + + public String getError() { + return error; + } + + public ErrorDetail getErrorDetail() { + return errorDetail; + } + + @JsonIgnoreProperties(ignoreUnknown=true) + public static class ErrorDetail implements Serializable { + @JsonProperty("code") + String code; + @JsonProperty("message") + String message; + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("code", code) + .add("message", message) + .toString(); + } + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("stream", stream) + .add("error", error) + .add("errorDetail", errorDetail) + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/ExposedPort.java b/src/main/java/com/github/dockerjava/api/model/ExposedPort.java new file mode 100644 index 00000000..30f7ce38 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/ExposedPort.java @@ -0,0 +1,191 @@ +package com.github.dockerjava.api.model; + +import static com.github.dockerjava.api.model.InternetProtocol.TCP; +import static com.github.dockerjava.api.model.InternetProtocol.UDP; + +import java.io.IOException; +import java.util.Map.Entry; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.NullNode; +import com.github.dockerjava.api.model.Ports.Binding; + +/** + * Represents a container port that Docker exposes to external clients. + * The port is defined by its {@link #getPort() port number} and an + * {@link InternetProtocol}. + * It can be published by Docker by {@link Ports#bind(ExposedPort, Binding) binding} + * it to a host port, represented by a {@link Binding}. + */ +@JsonDeserialize(using = ExposedPort.Deserializer.class) +@JsonSerialize(using = ExposedPort.Serializer.class) +public class ExposedPort { + + private final InternetProtocol protocol; + private final int port; + + /** + * Creates an {@link ExposedPort} for the given parameters. + * + * @param port the {@link #getPort() port number} + * @param protocol the {@link InternetProtocol} + */ + public ExposedPort(int port, InternetProtocol protocol) { + this.port = port; + this.protocol = protocol; + } + + /** + * Creates an {@link ExposedPort} for the given + * {@link #getPort() port number} and {@link InternetProtocol#DEFAULT}. + * + * @param port the {@link #getPort() port number} + */ + public ExposedPort(int port) { + this(port, InternetProtocol.DEFAULT); + } + + /** + * Creates an {@link ExposedPort} for the given parameters. + * + * @param scheme the {@link #getScheme() scheme}, tcp or + * udp + * @param port the {@link #getPort() port number} + * @deprecated use {@link #ExposedPort(int, InternetProtocol)} + */ + @Deprecated + public ExposedPort(String scheme, int port) { + this(port, InternetProtocol.valueOf(scheme)); + } + + /** @return the {@link InternetProtocol} of the {@link #getPort() port} + * that the container exposes */ + public InternetProtocol getProtocol() { + return protocol; + } + + /** + * @return the scheme (internet protocol), tcp or udp + * @deprecated use {@link #getProtocol()} + */ + @Deprecated + public String getScheme() { + return protocol.toString(); + } + + /** @return the port number that the container exposes */ + public int getPort() { + return port; + } + + /** + * Creates an {@link ExposedPort} for {@link InternetProtocol#TCP}. + * This is a shortcut for new ExposedPort(port, {@link InternetProtocol#TCP}) + */ + public static ExposedPort tcp(int port) { + return new ExposedPort(port, TCP); + } + + /** + * Creates an {@link ExposedPort} for {@link InternetProtocol#UDP}. + * This is a shortcut for new ExposedPort(port, {@link InternetProtocol#UDP}) + */ + public static ExposedPort udp(int port) { + return new ExposedPort(port, UDP); + } + + /** + * Parses a textual port specification (as used by the Docker CLI) to an + * {@link ExposedPort}. + * + * @param serialized the specification, e.g. 80/tcp + * @return an {@link ExposedPort} matching the specification + * @throws IllegalArgumentException if the specification cannot be parsed + */ + public static ExposedPort parse(String serialized) throws IllegalArgumentException { + try { + String[] parts = serialized.split("/"); + switch (parts.length) { + case 1: + return new ExposedPort(Integer.valueOf(parts[0])); + case 2: + return new ExposedPort(Integer.valueOf(parts[0]), InternetProtocol.parse(parts[1])); + default: + throw new IllegalArgumentException(); + } + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing ExposedPort '" + serialized + "'"); + } + } + + /** + * Returns a string representation of this {@link ExposedPort} suitable + * for inclusion in a JSON message. + * The format is port/protocol, like the argument in {@link #parse(String)}. + * + * @return a string representation of this {@link ExposedPort} + */ + @Override + public String toString() { + return port + "/" + protocol.toString(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ExposedPort) { + ExposedPort other = (ExposedPort) obj; + return new EqualsBuilder().append(protocol, other.getProtocol()) + .append(port, other.getPort()).isEquals(); + } else + return super.equals(obj); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(protocol).append(port).toHashCode(); + } + + public static class Deserializer extends JsonDeserializer { + @Override + public ExposedPort deserialize(JsonParser jsonParser, + DeserializationContext deserializationContext) + throws IOException, JsonProcessingException { + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + if (!node.equals(NullNode.getInstance())) { + Entry field = node.fields().next(); + return ExposedPort.parse(field.getKey()); + } else { + return null; + } + } + } + + public static class Serializer extends JsonSerializer { + + @Override + public void serialize(ExposedPort exposedPort, JsonGenerator jsonGen, + SerializerProvider serProvider) throws IOException, + JsonProcessingException { + + jsonGen.writeStartObject(); + jsonGen.writeFieldName(exposedPort.toString()); + jsonGen.writeEndObject(); + } + + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/ExposedPorts.java b/src/main/java/com/github/dockerjava/api/model/ExposedPorts.java new file mode 100644 index 00000000..983ad3c3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/ExposedPorts.java @@ -0,0 +1,77 @@ +package com.github.dockerjava.api.model; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.NullNode; + + +@JsonSerialize(using = ExposedPorts.Serializer.class) +@JsonDeserialize(using = ExposedPorts.Deserializer.class) +public class ExposedPorts { + + private ExposedPort[] exposedPorts; + + public ExposedPorts(ExposedPort... exposedPorts) { + this.exposedPorts = exposedPorts; + } + + public ExposedPorts(List exposedPorts) { + this.exposedPorts = exposedPorts.toArray(new ExposedPort[exposedPorts.size()]); + } + + public ExposedPort[] getExposedPorts() { + return exposedPorts; + } + + public static class Serializer extends JsonSerializer { + + @Override + public void serialize(ExposedPorts exposedPorts, JsonGenerator jsonGen, + SerializerProvider serProvider) throws IOException, + JsonProcessingException { + + jsonGen.writeStartObject(); + for (ExposedPort exposedPort : exposedPorts.getExposedPorts()) { + jsonGen.writeFieldName(exposedPort.toString()); + jsonGen.writeStartObject(); + jsonGen.writeEndObject(); + } + jsonGen.writeEndObject(); + } + + } + + public static class Deserializer extends JsonDeserializer { + @Override + public ExposedPorts deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + + List exposedPorts = new ArrayList(); + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + for (Iterator> it = node.fields(); it.hasNext();) { + + Map.Entry field = it.next(); + if (!field.getValue().equals(NullNode.getInstance())) { + exposedPorts.add(ExposedPort.parse(field.getKey())); + } + } + return new ExposedPorts(exposedPorts.toArray(new ExposedPort[0])); + } + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/Frame.java b/src/main/java/com/github/dockerjava/api/model/Frame.java new file mode 100644 index 00000000..175b5683 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Frame.java @@ -0,0 +1,47 @@ +package com.github.dockerjava.api.model; + +import java.util.Arrays; + +/** + * Represents a logging frame. + */ +public class Frame { + private final StreamType streamType; + private final byte[] payload; + + public Frame(StreamType streamType, byte[] payload) { + this.streamType = streamType; + this.payload = payload; + } + + public StreamType getStreamType() { + return streamType; + } + + public byte[] getPayload() { + return payload; + } + + @Override + public String toString() { + return String.format("%s: %s", streamType, new String(payload).trim()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Frame frame = (Frame) o; + + return streamType == frame.streamType && Arrays.equals(payload, frame.payload); + + } + + @Override + public int hashCode() { + int result = streamType.hashCode(); + result = 31 * result + Arrays.hashCode(payload); + return result; + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/HostConfig.java b/src/main/java/com/github/dockerjava/api/model/HostConfig.java new file mode 100644 index 00000000..9973e625 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/HostConfig.java @@ -0,0 +1,235 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang.builder.ToStringBuilder; + + +@JsonIgnoreProperties(ignoreUnknown = true) +public class HostConfig { + + @JsonProperty("Binds") + private Binds binds; + + @JsonProperty("Links") + private Links links; + + @JsonProperty("LxcConf") + private LxcConf[] lxcConf; + + @JsonProperty("PortBindings") + private Ports portBindings; + + @JsonProperty("PublishAllPorts") + private boolean publishAllPorts; + + @JsonProperty("Privileged") + private boolean privileged; + + @JsonProperty("Dns") + private String[] dns; + + @JsonProperty("DnsSearch") + private String[] dnsSearch; + + @JsonProperty("VolumesFrom") + private VolumesFrom[] volumesFrom; + + @JsonProperty("ContainerIDFile") + private String containerIDFile; + + @JsonProperty("CapAdd") + private Capability[] capAdd; + + @JsonProperty("CapDrop") + private Capability[] capDrop; + + @JsonProperty("RestartPolicy") + private RestartPolicy restartPolicy; + + @JsonProperty("NetworkMode") + private String networkMode; + + @JsonProperty("Devices") + private Device[] devices; + + @JsonProperty("ExtraHosts") + private String[] extraHosts; + + @JsonProperty("Ulimits") + private Ulimit[] ulimits; + + public HostConfig() { + } + + public HostConfig(Bind[] binds, Link[] links, LxcConf[] lxcConf, Ports portBindings, boolean publishAllPorts, + boolean privileged, String[] dns, String[] dnsSearch, VolumesFrom[] volumesFrom, String containerIDFile, + Capability[] capAdd, Capability[] capDrop, RestartPolicy restartPolicy, String networkMode, Device[] devices, + String[] extraHosts, Ulimit[] ulimits) { + this.binds = new Binds(binds); + this.links = new Links(links); + this.lxcConf = lxcConf; + this.portBindings = portBindings; + this.publishAllPorts = publishAllPorts; + this.privileged = privileged; + this.dns = dns; + this.dnsSearch = dnsSearch; + this.volumesFrom = volumesFrom; + this.containerIDFile = containerIDFile; + this.capAdd = capAdd; + this.capDrop = capDrop; + this.restartPolicy = restartPolicy; + this.networkMode = networkMode; + this.devices = devices; + this.extraHosts = extraHosts; + this.ulimits = ulimits; + } + + + @JsonIgnore + public Bind[] getBinds() { + return (binds == null) ? new Bind[0] : binds.getBinds(); + } + + public LxcConf[] getLxcConf() { + return lxcConf; + } + + public Ports getPortBindings() { + return portBindings; + } + + public boolean isPublishAllPorts() { + return publishAllPorts; + } + + public boolean isPrivileged() { + return privileged; + } + + public String[] getDns() { + return dns; + } + + public VolumesFrom[] getVolumesFrom() { + return volumesFrom; + } + + public String getContainerIDFile() { + return containerIDFile; + } + + public String[] getDnsSearch() { + return dnsSearch; + } + + @JsonIgnore + public Link[] getLinks() { + return (links == null) ? new Link[0] : links.getLinks(); + } + + public String getNetworkMode() { + return networkMode; + } + + public Device[] getDevices() { + return devices; + } + + public String[] getExtraHosts() { + return extraHosts; + } + + public RestartPolicy getRestartPolicy() { + return restartPolicy; + } + + public Capability[] getCapAdd() { + return capAdd; + } + + public Capability[] getCapDrop() { + return capDrop; + } + + public Ulimit[] getUlimits() { + return ulimits; + } + + @JsonIgnore + public void setBinds(Bind... binds) { + this.binds = new Binds(binds); + } + + @JsonIgnore + public void setLinks(Link... links) { + this.links = new Links(links); + } + + public void setLxcConf(LxcConf[] lxcConf) { + this.lxcConf = lxcConf; + } + + public void setPortBindings(Ports portBindings) { + this.portBindings = portBindings; + } + + public void setPublishAllPorts(boolean publishAllPorts) { + this.publishAllPorts = publishAllPorts; + } + + public void setPrivileged(boolean privileged) { + this.privileged = privileged; + } + + public void setDns(String[] dns) { + this.dns = dns; + } + + public void setDnsSearch(String[] dnsSearch) { + this.dnsSearch = dnsSearch; + } + + public void setVolumesFrom(VolumesFrom[] volumesFrom) { + this.volumesFrom = volumesFrom; + } + + public void setContainerIDFile(String containerIDFile) { + this.containerIDFile = containerIDFile; + } + + public void setCapAdd(Capability[] capAdd) { + this.capAdd = capAdd; + } + + public void setCapDrop(Capability[] capDrop) { + this.capDrop = capDrop; + } + + public void setRestartPolicy(RestartPolicy restartPolicy) { + this.restartPolicy = restartPolicy; + } + + public void setNetworkMode(String networkMode) { + this.networkMode = networkMode; + } + + public void setDevices(Device[] devices) { + this.devices = devices; + } + + public void setExtraHosts(String[] extraHosts) { + this.extraHosts = extraHosts; + } + + public void setUlimits(Ulimit[] ulimits) { + this.ulimits = ulimits; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/Identifier.java b/src/main/java/com/github/dockerjava/api/model/Identifier.java new file mode 100644 index 00000000..498104e2 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Identifier.java @@ -0,0 +1,59 @@ +package com.github.dockerjava.api.model; + + +import com.google.common.base.Objects; +import com.google.common.base.Optional; + +/** + * Created by magnayn on 22/07/2014. + */ +public class Identifier { + public final Repository repository; + public final Optional tag; + + public Identifier(Repository repository, String tag) { + this.repository = repository; + + if( tag == null ) + this.tag = Optional.absent(); + else + this.tag = Optional.of(tag); + } + + + /** + * Return an identifier that correctly splits up the repository and tag. + * There can be > 1 ":" + * fred/jim --> fred/jim, [] + * fred/jim:123 --> fred/jim, 123 + * fred:123/jim:123 --> fred:123/jim, 123 + * + * + * @param identifier as a string + * @return parsed identifier. + */ + public static Identifier fromCompoundString(String identifier) { + String[] parts = identifier.split("/"); + if( parts.length != 2 ) { + String[] rhs = identifier.split(":"); + if( rhs.length != 2 ) + return new Identifier( new Repository(identifier), null); + else + return new Identifier( new Repository(rhs[0]), rhs[1]); + } + + String[] rhs = parts[1].split(":"); + if( rhs.length != 2 ) + return new Identifier( new Repository(identifier), null); + + return new Identifier( new Repository(parts[0] + "/" + rhs[0]), rhs[1]); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("repository", repository) + .add("tag", tag) + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Image.java b/src/main/java/com/github/dockerjava/api/model/Image.java new file mode 100644 index 00000000..8c694dab --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Image.java @@ -0,0 +1,62 @@ +package com.github.dockerjava.api.model; + +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Image { + + @JsonProperty("Created") + private long created; + + @JsonProperty("Id") + private String id; + + @JsonProperty("ParentId") + private String parentId; + + @JsonProperty("RepoTags") + private String[] repoTags; + + @JsonProperty("Size") + private long size; + + @JsonProperty("VirtualSize") + private long virtualSize; + + public String getId() { + return id; + } + + public String[] getRepoTags() { + return repoTags; + } + + public String getParentId() { + return parentId; + } + + public long getCreated() { + return created; + } + + public long getSize() { + return size; + } + + public long getVirtualSize() { + return virtualSize; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Info.java b/src/main/java/com/github/dockerjava/api/model/Info.java new file mode 100644 index 00000000..fb63f2ff --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Info.java @@ -0,0 +1,195 @@ +package com.github.dockerjava.api.model; + +import java.util.List; + +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Info { + + @JsonProperty("Containers") + private int containers; + + @JsonProperty("Debug") + private boolean debug; + + @JsonProperty("DockerRootDir") + private String DockerRootDir; + + @JsonProperty("Driver") + private String driver; + + @JsonProperty("DriverStatus") + private List driverStatuses; + + @JsonProperty("ExecutionDriver") + private String executionDriver; + + @JsonProperty("ID") + private String ID; + + @JsonProperty("IPv4Forwarding") + private boolean IPv4Forwarding; + + @JsonProperty("Images") + private int images; + + @JsonProperty("IndexServerAddress") + private String IndexServerAddress; + + @JsonProperty("InitPath") + private String initPath; + + @JsonProperty("InitSha1") + private String initSha1; + + @JsonProperty("KernelVersion") + private String kernelVersion; + + @JsonProperty("Labels") + private String Labels; + + @JsonProperty("MemoryLimit") + private boolean memoryLimit; + + @JsonProperty("MemTotal") + private long memTotal; + + @JsonProperty("Name") + private String name; + + @JsonProperty("NCPU") + private int NCPU; + + @JsonProperty("NEventsListener") + private long nEventListener; + + @JsonProperty("NFd") + private int NFd; + + @JsonProperty("NGoroutines") + private int NGoroutines; + + @JsonProperty("OperatingSystem") + private String OperatingSystem; + + @JsonProperty("Sockets") + private String[] sockets; + + @JsonProperty("SwapLimit") + private boolean swapLimit; + + public boolean isDebug() { + return debug; + } + + public int getContainers() { + return containers; + } + + public String getDockerRootDir() { + return DockerRootDir; + } + + public String getDriver() { + return driver; + } + + public List getDriverStatuses() { + return driverStatuses; + } + + public int getImages() { + return images; + } + + public String getID() { + return ID; + } + + public boolean getIPv4Forwarding() { + return IPv4Forwarding; + } + + public String getIndexServerAddress() { + return IndexServerAddress; + } + + public String getInitPath() { + return initPath; + } + + public String getInitSha1() { + return initSha1; + } + + public String getKernelVersion() { + return kernelVersion; + } + + public String getLabels() { + return Labels; + } + + public String[] getSockets() { + return sockets; + } + + public boolean isMemoryLimit() { + return memoryLimit; + } + + public long getnEventListener() { + return nEventListener; + } + + public long getMemTotal() { + return memTotal; + } + + public String getName() { + return name; + } + + public int getNCPU() { + return NCPU; + } + + public int getNFd() { + return NFd; + } + + public int getNGoroutines() { + return NGoroutines; + } + + public String getOperatingSystem() { + return OperatingSystem; + } + + public boolean getSwapLimit() { + return swapLimit; + } + + public String getExecutionDriver() { + return executionDriver; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/InternetProtocol.java b/src/main/java/com/github/dockerjava/api/model/InternetProtocol.java new file mode 100644 index 00000000..96c21524 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/InternetProtocol.java @@ -0,0 +1,49 @@ +package com.github.dockerjava.api.model; + + +/** + * The IP protocols supported by Docker. + * + * @see #TCP + * @see #UDP + */ +public enum InternetProtocol { + /** The Transmission Control Protocol */ + TCP, + + /** The User Datagram Protocol */ + UDP; + + /** + * The default {@link InternetProtocol}: {@link #TCP} + */ + public static final InternetProtocol DEFAULT = TCP; + + /** + * Returns a string representation of this {@link InternetProtocol} suitable + * for inclusion in a JSON message. + * The output is the lowercased name of the Protocol, e.g. tcp. + * + * @return a string representation of this {@link InternetProtocol} + */ + @Override + public String toString() { + return super.toString().toLowerCase(); + } + + /** + * Parses a string to an {@link InternetProtocol}. + * + * @param serialized the protocol, e.g. tcp or TCP + * @return an {@link InternetProtocol} described by the string + * @throws IllegalArgumentException if the argument cannot be parsed + */ + public static InternetProtocol parse(String serialized) throws IllegalArgumentException { + try { + return valueOf(serialized.toUpperCase()); + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing Protocol '" + serialized + "'"); + } + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/Link.java b/src/main/java/com/github/dockerjava/api/model/Link.java new file mode 100644 index 00000000..ab8485a3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Link.java @@ -0,0 +1,107 @@ +package com.github.dockerjava.api.model; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +/** + * Represents a network link between two Docker containers. + * The container with the name {@link #getName()} is made available in the + * target container with the aliased name {@link #getAlias()}. + * This involves creating an entry in /etc/hosts and some environment + * variables in the target container as well as creating a network bridge between + * both containers. + */ +public class Link +{ + + private final String name; + + private final String alias; + + /** + * Creates a {@link Link} for the container with the given name and an aliased + * name for use in the target container. + * + * @param name the name of the container that you want to link into the target + * container + * @param alias the aliased name under which the linked container will be available + * in the target container + */ + public Link(final String name, final String alias) + { + this.name = name; + this.alias = alias; + } + + /** + * @return the name of the container that is linked into the target container + */ + public String getName() + { + return name; + } + + /** + * @return the aliased name under which the linked container will be available + * in the target container + */ + public String getAlias() + { + return alias; + } + + /** + * Parses a textual link specification (as used by the Docker CLI) to a {@link Link}. + * + * @param serialized the specification, e.g. name:alias or /name1:/name2/alias + * @return a {@link Link} matching the specification + * @throws IllegalArgumentException if the specification cannot be parsed + */ + public static Link parse(final String serialized) throws IllegalArgumentException + { + try { + final String[] parts = serialized.split(":"); + switch (parts.length) { + case 2: { + String[] nameSplit = parts[0].split("/"); + String[] aliasSplit = parts[1].split("/"); + return new Link(nameSplit[nameSplit.length - 1], aliasSplit[aliasSplit.length - 1]); + } + default: { + throw new IllegalArgumentException(); + } + } + } catch (final Exception e) { + throw new IllegalArgumentException("Error parsing Link '" + serialized + "'"); + } + } + + @Override + public boolean equals(final Object obj) + { + if (obj instanceof Link) { + final Link other = (Link) obj; + return new EqualsBuilder().append(name, other.getName()).append(alias, other.getAlias()).isEquals(); + } else + return super.equals(obj); + } + + @Override + public int hashCode() + { + return new HashCodeBuilder().append(name).append(alias).toHashCode(); + } + + /** + * Returns a string representation of this {@link Link} suitable + * for inclusion in a JSON message. + * The format is name:alias, like the argument in {@link #parse(String)}. + * + * @return a string representation of this {@link Link} + */ + @Override + public String toString() { + return name + ":" + alias; + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/Links.java b/src/main/java/com/github/dockerjava/api/model/Links.java new file mode 100644 index 00000000..888c0aa2 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Links.java @@ -0,0 +1,78 @@ + +package com.github.dockerjava.api.model; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.NullNode; + +@JsonSerialize(using = Links.Serializer.class) +@JsonDeserialize(using = Links.Deserializer.class) +public class Links +{ + + private final Link[] links; + + public Links(final Link... links) + { + this.links = links; + } + + public Links(final List links) { + this.links = links.toArray(new Link[links.size()]); + } + + public Link[] getLinks() + { + return links; + } + + public static class Serializer extends JsonSerializer + { + + @Override + public void serialize(final Links links, final JsonGenerator jsonGen, final SerializerProvider serProvider) throws IOException, JsonProcessingException + { + jsonGen.writeStartArray(); + for (final Link link : links.getLinks()) { + jsonGen.writeString(link.toString()); + } + jsonGen.writeEndArray(); + } + + } + + public static class Deserializer extends JsonDeserializer + { + + @Override + public Links deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException + { + final List binds = new ArrayList(); + final ObjectCodec oc = jsonParser.getCodec(); + final JsonNode node = oc.readTree(jsonParser); + for (final Iterator it = node.elements(); it.hasNext();) { + + final JsonNode element = it.next(); + if (!element.equals(NullNode.getInstance())) { + binds.add(Link.parse(element.asText())); + } + } + return new Links(binds.toArray(new Link[0])); + } + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/LxcConf.java b/src/main/java/com/github/dockerjava/api/model/LxcConf.java new file mode 100644 index 00000000..67ce00ca --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/LxcConf.java @@ -0,0 +1,38 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class LxcConf { + @JsonProperty("Key") + public String key; + + @JsonProperty("Value") + public String value; + + public LxcConf(String key, String value) { + this.key = key; + this.value = value; + } + + public LxcConf() { + } + + public String getKey() { + return key; + } + + public LxcConf setKey(String key) { + this.key = key; + return this; + } + + public String getValue() { + return value; + } + + public LxcConf setValue(String value) { + this.value = value; + return this; + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/PortBinding.java b/src/main/java/com/github/dockerjava/api/model/PortBinding.java new file mode 100644 index 00000000..13c91bdb --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/PortBinding.java @@ -0,0 +1,82 @@ +package com.github.dockerjava.api.model; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +import com.github.dockerjava.api.command.InspectContainerResponse.NetworkSettings; +import com.github.dockerjava.api.model.Ports.Binding; + +/** + * In a {@link PortBinding}, a network socket on the Docker host, expressed + * as a {@link Binding}, is bound to an {@link ExposedPort} of a container. + * A {@link PortBinding} corresponds to the --publish + * (-p) option of the docker run (and similar) + * CLI command for adding port bindings to a container. + *

+ * Note: This is an abstraction used for creating new port bindings. + * It is not to be confused with the abstraction used for querying existing + * port bindings from a container configuration in + * {@link NetworkSettings#getPorts()} and {@link HostConfig#getPortBindings()}. + * In that context, a Map<ExposedPort, Binding[]> is used. + */ +public class PortBinding { + private final Binding binding; + private final ExposedPort exposedPort; + + public PortBinding(Binding binding, ExposedPort exposedPort) { + this.binding = binding; + this.exposedPort = exposedPort; + } + + public Binding getBinding() { + return binding; + } + + public ExposedPort getExposedPort() { + return exposedPort; + } + + public static PortBinding parse(String serialized) throws IllegalArgumentException { + try { + String[] parts = StringUtils.splitByWholeSeparator(serialized, ":"); + switch (parts.length) { + case 3: + // 127.0.0.1:80:8080/tcp + return createFromSubstrings(parts[0] + ":" + parts[1], parts[2]); + case 2: + // 80:8080 // 127.0.0.1::8080 + return createFromSubstrings(parts[0], parts[1]); + case 1: + // 8080 + return createFromSubstrings("", parts[0]); + default: + throw new IllegalArgumentException(); + } + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing PortBinding '" + + serialized + "'", e); + } + } + + private static PortBinding createFromSubstrings(String binding, String exposedPort) + throws IllegalArgumentException { + return new PortBinding(Binding.parse(binding), ExposedPort.parse(exposedPort)); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PortBinding) { + PortBinding other = (PortBinding) obj; + return new EqualsBuilder().append(binding, other.getBinding()) + .append(exposedPort, other.getExposedPort()).isEquals(); + } else + return super.equals(obj); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(binding).append(exposedPort).toHashCode(); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/Ports.java b/src/main/java/com/github/dockerjava/api/model/Ports.java new file mode 100644 index 00000000..0c2df94a --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Ports.java @@ -0,0 +1,333 @@ +package com.github.dockerjava.api.model; + +import static org.apache.commons.lang.StringUtils.isEmpty; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.builder.EqualsBuilder; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.NullNode; +import com.github.dockerjava.api.command.InspectContainerResponse.NetworkSettings; + +/** + * A container for port bindings, made available as a {@link Map} via its + * {@link #getBindings()} method. + *

+ * Note: This is an abstraction used for querying existing port bindings from + * a container configuration. + * It is not to be confused with the {@link PortBinding} abstraction used for + * adding new port bindings to a container. + * + * @see HostConfig#getPortBindings() + * @see NetworkSettings#getPorts() + */ +@JsonDeserialize(using = Ports.Deserializer.class) +@JsonSerialize(using = Ports.Serializer.class) +public class Ports { + + private final Map ports = new HashMap(); + + /** + * Creates a {@link Ports} object with no {@link PortBinding}s. + * Use {@link #bind(ExposedPort, Binding)} or {@link #add(PortBinding...)} + * to add {@link PortBinding}s. + */ + public Ports() { } + + /** + * Creates a {@link Ports} object with an initial {@link PortBinding} for + * the specified {@link ExposedPort} and {@link Binding}. + * Use {@link #bind(ExposedPort, Binding)} or {@link #add(PortBinding...)} + * to add more {@link PortBinding}s. + */ + public Ports(ExposedPort exposedPort, Binding host) { + bind(exposedPort, host); + } + + public Ports(PortBinding... portBindings) { + add(portBindings); + } + + /** + * Adds a new {@link PortBinding} for the specified {@link ExposedPort} and + * {@link Binding} to the current bindings. + */ + public void bind(ExposedPort exposedPort, Binding binding) { + if (ports.containsKey(exposedPort)) { + Binding[] bindings = ports.get(exposedPort); + ports.put(exposedPort, (Binding[]) ArrayUtils.add(bindings, binding)); + } else { + if (binding == null) { + ports.put(exposedPort, null); + } else { + ports.put(exposedPort, new Binding[]{binding}); + } + } + } + + /** + * Adds the specified {@link PortBinding}(s) to the list of {@link PortBinding}s. + */ + public void add(PortBinding... portBindings) { + for (PortBinding binding : portBindings) { + bind(binding.getExposedPort(), binding.getBinding()); + } + } + + @Override + public String toString(){ + return ports.toString(); + } + + /** + * Returns the port bindings in the format used by the Docker remote API, + * i.e. the {@link Binding}s grouped by {@link ExposedPort}. + * + * @return the port bindings as a {@link Map} that contains one or more + * {@link Binding}s per {@link ExposedPort}. + */ + public Map getBindings(){ + return ports; + } + +// public PortBinding[] getBindingsAsArray() { +// List bindings = new ArrayList<>(); +// for(Map.Entry entry: ports.entrySet()) { +// for(Ports.Binding binding : entry.getValue()) { +// bindings.add(new PortBinding(binding, entry.getKey())); +// } +// } +// return bindings.toArray(new PortBinding[bindings.size()]); +// } + + /** + * Creates a {@link Binding} for the given IP address and port number. + */ + public static Binding Binding(String hostIp, Integer hostPort) { + return new Binding(hostIp, hostPort); + } + + /** + * Creates a {@link Binding} for the given port number, leaving the + * IP address undefined. + */ + public static Binding Binding(Integer hostPort) { + return new Binding(hostPort); + } + + + /** + * A {@link Binding} represents a socket on the Docker host that is + * used in a {@link PortBinding}. + * It is characterized by an {@link #getHostIp() IP address} and a + * {@link #getHostPort() port number}. + * Both properties may be null in order to let Docker assign + * them dynamically/using defaults. + * + * @see Ports#bind(ExposedPort, Binding) + * @see ExposedPort + */ + public static class Binding { + + private final String hostIp; + + private final Integer hostPort; + + /** + * Creates a {@link Binding} for the given {@link #getHostIp() IP address} + * and {@link #getHostPort() port number}. + * + * @see Ports#bind(ExposedPort, Binding) + * @see ExposedPort + */ + public Binding(String hostIp, Integer hostPort) { + this.hostIp = isEmpty(hostIp) ? null : hostIp; + this.hostPort = hostPort; + } + + /** + * Creates a {@link Binding} for the given {@link #getHostPort() port number}, + * leaving the {@link #getHostIp() IP address} undefined. + * + * @see Ports#bind(ExposedPort, Binding) + * @see ExposedPort + */ + public Binding(Integer hostPort) { + this(null, hostPort); + } + + /** + * Creates a {@link Binding} for the given {@link #getHostIp() IP address}, + * leaving the {@link #getHostPort() port number} undefined. + */ + public Binding(String hostIp) { + this(hostIp, null); + } + + /** + * Creates a {@link Binding} with both {@link #getHostIp() IP address} and + * {@link #getHostPort() port number} undefined. + */ + public Binding() { + this(null, null); + } + + /** + * @return the IP address on the Docker host. + * May be null, in which case Docker will bind the + * port to all interfaces (0.0.0.0). + */ + public String getHostIp() { + return hostIp; + } + + /** + * @return the port number on the Docker host. + * May be null, in which case Docker will dynamically + * assign a port. + */ + public Integer getHostPort() { + return hostPort; + } + + /** + * Parses a textual host and port specification (as used by the Docker CLI) + * to a {@link Binding}. + *

+ * Legal syntax: IP|IP:port|port + * + * @param serialized serialized the specification, e.g. + * 127.0.0.1:80 + * @return a {@link Binding} matching the specification + * @throws IllegalArgumentException if the specification cannot be parsed + */ + public static Binding parse(String serialized) throws IllegalArgumentException { + try { + if (serialized.isEmpty()) { + return new Binding(); + } + + String[] parts = serialized.split(":"); + switch (parts.length) { + case 2: { + return new Binding(parts[0], Integer.valueOf(parts[1])); + } + case 1: { + return parts[0].contains(".") ? new Binding(parts[0]) + : new Binding(Integer.valueOf(parts[0])); + } + default: { + throw new IllegalArgumentException(); + } + } + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing Binding '" + + serialized + "'"); + } + } + + /** + * Returns a string representation of this {@link Binding} suitable + * for inclusion in a JSON message. + * The format is [IP:]Port, like the argument in {@link #parse(String)}. + * + * @return a string representation of this {@link Binding} + */ + @Override + public String toString() { + if (isEmpty(hostIp)) { + return Integer.toString(hostPort); + } else if (hostPort == null) { + return hostIp; + } else { + return hostIp + ":" + hostPort; + } + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Binding) { + Binding other = (Binding) obj; + return new EqualsBuilder() + .append(hostIp, other.getHostIp()) + .append(hostPort, other.getHostPort()).isEquals(); + } else + return super.equals(obj); + } + } + + + public static class Deserializer extends JsonDeserializer { + @Override + public Ports deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + + Ports out = new Ports(); + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + for (Iterator> it = node.fields(); it.hasNext();) { + + Map.Entry portNode = it.next(); + JsonNode bindingsArray = portNode.getValue(); + if (bindingsArray.equals(NullNode.getInstance())) { + out.bind(ExposedPort.parse(portNode.getKey()), null); + } else { + for (int i = 0; i < bindingsArray.size(); i++) { + JsonNode bindingNode = bindingsArray.get(i); + if (!bindingNode.equals(NullNode.getInstance())) { + String hostIp = bindingNode.get("HostIp").textValue(); + int hostPort = bindingNode.get("HostPort").asInt(); + out.bind(ExposedPort.parse(portNode.getKey()), new Binding(hostIp, hostPort)); + } + } + } + } + return out; + } + } + + public static class Serializer extends JsonSerializer { + + @Override + public void serialize(Ports portBindings, JsonGenerator jsonGen, + SerializerProvider serProvider) throws IOException, JsonProcessingException { + + jsonGen.writeStartObject(); + for(Entry entry : portBindings.getBindings().entrySet()){ + jsonGen.writeFieldName(entry.getKey().toString()); + if (entry.getValue() != null) { + jsonGen.writeStartArray(); + for (Binding binding : entry.getValue()) { + jsonGen.writeStartObject(); + jsonGen.writeStringField("HostIp", binding.getHostIp() == null ? "" : binding.getHostIp()); + jsonGen.writeStringField("HostPort", binding.getHostPort() == null ? "" : binding.getHostPort().toString()); + jsonGen.writeEndObject(); + } + jsonGen.writeEndArray(); + } else { + jsonGen.writeNull(); + } + } + jsonGen.writeEndObject(); + } + + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/PullEventStreamItem.java b/src/main/java/com/github/dockerjava/api/model/PullEventStreamItem.java new file mode 100644 index 00000000..4a3a0b50 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/PullEventStreamItem.java @@ -0,0 +1,59 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; + +import java.io.Serializable; + +/** + * Represents an item returned from pull + */ +@JsonIgnoreProperties(ignoreUnknown=true) +public class PullEventStreamItem implements Serializable { + + private static final long serialVersionUID = -5187169652557467828L; + + @JsonProperty("status") + private String status; + + @JsonProperty("progress") + private String progress; + + @JsonProperty("progressDetail") + private ProgressDetail progressDetail; + + + public String getStatus() { + return status; + } + + public String getProgress() { + return progress; + } + + public ProgressDetail getProgressDetail() { + return progressDetail; + } + + @JsonIgnoreProperties(ignoreUnknown=true) + public static class ProgressDetail implements Serializable { + @JsonProperty("current") + int current; + + + @Override + public String toString() { + return "current " + current; + } + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("status", status) + .add("progress", progress) + .add("progressDetail", progressDetail) + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/PushEventStreamItem.java b/src/main/java/com/github/dockerjava/api/model/PushEventStreamItem.java new file mode 100644 index 00000000..c8aac4c9 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/PushEventStreamItem.java @@ -0,0 +1,60 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; + +import com.google.common.base.Objects; + +/** + * Represents an item returned from push + */ +@JsonIgnoreProperties(ignoreUnknown=true) +public class PushEventStreamItem implements Serializable { + + private static final long serialVersionUID = -5187169652557467828L; + + @JsonProperty("status") + private String status; + + @JsonProperty("progress") + private String progress; + + @JsonProperty("progressDetail") + private ProgressDetail progressDetail; + + + public String getStatus() { + return status; + } + + public String getProgress() { + return progress; + } + + public ProgressDetail getProgressDetail() { + return progressDetail; + } + + @JsonIgnoreProperties(ignoreUnknown=true) + public static class ProgressDetail implements Serializable { + @JsonProperty("current") + int current; + + + @Override + public String toString() { + return "current " + current; + } + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("status", status) + .add("progress", progress) + .add("progressDetail", progressDetail) + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Repository.java b/src/main/java/com/github/dockerjava/api/model/Repository.java new file mode 100644 index 00000000..673eabee --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Repository.java @@ -0,0 +1,46 @@ +package com.github.dockerjava.api.model; + +import com.google.common.base.Objects; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * A repository or image name. + */ +public class Repository { + public final String name; + + /** + * Name may be eg. 'busybox' or '10.0.0.1:5000/fred' + * @param name Repository name + */ + public Repository(String name) { + this.name = name; + } + + /** + * Return the URL portion (repository). + * Note that this might not actually BE a repository location. + * @return + * @throws java.net.MalformedURLException + */ + public URL getURL() throws MalformedURLException { + return new URL("http://" + name); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("name", name) + .toString(); + } + + + public String getPath() { + if( !name.contains("/") ) + return name; + + return name.substring(name.indexOf("/") + 1 ); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java b/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java new file mode 100644 index 00000000..bb933f18 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java @@ -0,0 +1,136 @@ +package com.github.dockerjava.api.model; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Container restart policy + * + *

+ *
no
+ *
Do not restart the container if it dies. (default)
+ * + *
on-failure
+ *
Restart the container if it exits with a non-zero exit code. + * Can also accept an optional maximum restart count (e.g. on-failure:5).
+ * + *
always
+ *
Always restart the container no matter what exit code is returned.
+ *
+ * + * @author marcus + * + */ +public class RestartPolicy { + + @JsonProperty("MaximumRetryCount") + private int maximumRetryCount = 0; + + @JsonProperty("Name") + private String name = ""; + + public RestartPolicy() { + } + + private RestartPolicy(int maximumRetryCount, String name) { + checkNotNull(name, "name is null"); + this.maximumRetryCount = maximumRetryCount; + this.name = name; + } + + /** + * Do not restart the container if it dies. (default) + */ + public static RestartPolicy noRestart() { + return new RestartPolicy(); + } + + /** + * Always restart the container no matter what exit code is returned. + */ + public static RestartPolicy alwaysRestart() { + return new RestartPolicy(0, "always"); + } + + /** + * Restart the container if it exits with a non-zero exit code. + * + * @param maximumRetryCount the maximum number of restarts. + * Set to 0 for unlimited retries. + */ + public static RestartPolicy onFailureRestart(int maximumRetryCount) { + return new RestartPolicy(maximumRetryCount, "on-failure"); + } + + public int getMaximumRetryCount() { + return maximumRetryCount; + } + + public String getName() { + return name; + } + + /** + * Parses a textual restart polixy specification (as used by the Docker CLI) + * to a {@link RestartPolicy}. + * + * @param serialized the specification, e.g. on-failure:2 + * @return a {@link RestartPolicy} matching the specification + * @throws IllegalArgumentException if the specification cannot be parsed + */ + public static RestartPolicy parse(String serialized) throws IllegalArgumentException { + try { + String[] parts = serialized.split(":"); + String name = parts[0]; + if ("no".equals(name)) + return noRestart(); + if ("always".equals(name)) + return alwaysRestart(); + if ("on-failure".equals(name)) { + int count = 0; + if (parts.length == 2) { + count = Integer.parseInt(parts[1]); + } + return onFailureRestart(count); + } + throw new IllegalArgumentException(); + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing RestartPolicy '" + serialized + "'"); + } + } + + /** + * Returns a string representation of this {@link RestartPolicy}. + * The format is name[:count], like the argument in {@link #parse(String)}. + * + * @return a string representation of this {@link RestartPolicy} + */ + @Override + public String toString() { + String result = name.isEmpty() ? "no" : name; + return maximumRetryCount > 0 ? result + ":" + maximumRetryCount : result; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof RestartPolicy) { + RestartPolicy other = (RestartPolicy) obj; + return new EqualsBuilder() + .append(maximumRetryCount, other.getMaximumRetryCount()) + .append(name, other.getName()) + .isEquals(); + } else + return super.equals(obj); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(maximumRetryCount) + .append(name).toHashCode(); + } + +} diff --git a/src/main/java/com/kpelykh/docker/client/model/SearchItem.java b/src/main/java/com/github/dockerjava/api/model/SearchItem.java similarity index 82% rename from src/main/java/com/kpelykh/docker/client/model/SearchItem.java rename to src/main/java/com/github/dockerjava/api/model/SearchItem.java index d7f6eb4f..cb4c5d7b 100644 --- a/src/main/java/com/kpelykh/docker/client/model/SearchItem.java +++ b/src/main/java/com/github/dockerjava/api/model/SearchItem.java @@ -1,54 +1,54 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class SearchItem { - - @JsonProperty("star_count") - private int starCount; - - @JsonProperty("is_official") - private boolean isOfficial; - - @JsonProperty("is_trusted") - private boolean isTrusted; - - @JsonProperty("name") - private String name; - - @JsonProperty("description") - private String description; - - public int getStarCount() { - return starCount; - } - - public boolean isOfficial() { - return isOfficial; - } - - public boolean isTrusted() { - return isTrusted; - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - @Override - public String toString() { - return "name='" + name + '\'' + - ", description='" + description + '\'' + '}'; - } -} +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang.builder.ToStringBuilder; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class SearchItem { + + @JsonProperty("star_count") + private int starCount; + + @JsonProperty("is_official") + private boolean isOfficial; + + @JsonProperty("is_trusted") + private boolean isTrusted; + + @JsonProperty("name") + private String name; + + @JsonProperty("description") + private String description; + + public int getStarCount() { + return starCount; + } + + public boolean isOfficial() { + return isOfficial; + } + + public boolean isTrusted() { + return isTrusted; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Stats.java b/src/main/java/com/github/dockerjava/api/model/Stats.java new file mode 100644 index 00000000..8c47c3d3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Stats.java @@ -0,0 +1,40 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author Heng WU(wuheng09@otcaix.iscas.ac.cn) + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Stats { + + @JsonProperty("read") + private String read; + + @JsonProperty("network") + private Object network; + + @JsonProperty("cpu_stats") + private Object cpu_stats; + + @JsonProperty("memory_stats") + private Object memory_stats; + + public String getRead() { + return read; + } + + public Object getNetwork() { + return network; + } + + public Object getCpu_stats() { + return cpu_stats; + } + + public Object getMemory_stats() { + return memory_stats; + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/StreamType.java b/src/main/java/com/github/dockerjava/api/model/StreamType.java new file mode 100644 index 00000000..eb11c553 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/StreamType.java @@ -0,0 +1,7 @@ +package com.github.dockerjava.api.model; + +public enum StreamType { + STDIN, + STDOUT, + STDERR +} diff --git a/src/main/java/com/github/dockerjava/api/model/Ulimit.java b/src/main/java/com/github/dockerjava/api/model/Ulimit.java new file mode 100644 index 00000000..d5fac09c --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Ulimit.java @@ -0,0 +1,64 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @author Vangie Du (duwan@live.com) + */ +public class Ulimit { + + @JsonProperty("Name") + private String name ; + + @JsonProperty("Soft") + private int soft; + + @JsonProperty("Hard") + private int hard; + + public Ulimit() { + + } + + public Ulimit(String name, int soft, int hard) { + checkNotNull(name, "Name is null"); + + this.name = name; + this.soft = soft; + this.hard = hard; + } + + public String getName() { + return name; + } + + public int getSoft() { + return soft; + } + + public int getHard() { + return hard; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Ulimit) { + Ulimit other = (Ulimit) obj; + return new EqualsBuilder() + .append(name, other.getName()) + .append(soft, other.getSoft()) + .append(hard, other.getHard()).isEquals(); + } else + return super.equals(obj); + + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(name).append(soft).append(hard).toHashCode(); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Version.java b/src/main/java/com/github/dockerjava/api/model/Version.java new file mode 100644 index 00000000..4e02e2c0 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Version.java @@ -0,0 +1,68 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang.builder.ToStringBuilder; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Version { + + @JsonProperty("ApiVersion") + private String apiVersion; + + @JsonProperty("Arch") + private String arch; + + @JsonProperty("GitCommit") + private String gitCommit; + + @JsonProperty("GoVersion") + private String goVersion; + + @JsonProperty("KernelVersion") + private String kernelVersion; + + @JsonProperty("Os") + private String operatingSystem; + + @JsonProperty("Version") + private String version; + + public String getVersion() { + return version; + } + + public String getGitCommit() { + return gitCommit; + } + + public String getGoVersion() { + return goVersion; + } + + public String getKernelVersion() { + return kernelVersion; + } + + public String getArch() { + return arch; + } + + public String getOperatingSystem() { + return operatingSystem; + } + + public String getApiVersion() { + return apiVersion; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Volume.java b/src/main/java/com/github/dockerjava/api/model/Volume.java new file mode 100644 index 00000000..131a9a56 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Volume.java @@ -0,0 +1,58 @@ +package com.github.dockerjava.api.model; + +import java.io.IOException; +import java.util.Map.Entry; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.NullNode; + +/** + * Represents a bind mounted volume in a Docker container. + * + * @see Bind + */ +public class Volume { + + private String path; + + public Volume(String path) { + this.path = path; + } + + public String getPath() { + return path; + } + + @Override + public String toString() { + return getPath(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Volume) { + Volume other = (Volume) obj; + return new EqualsBuilder().append(path, other.getPath()).isEquals(); + } else + return super.equals(obj); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(path).toHashCode(); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/VolumeBind.java b/src/main/java/com/github/dockerjava/api/model/VolumeBind.java new file mode 100644 index 00000000..41028b6d --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/VolumeBind.java @@ -0,0 +1,24 @@ +package com.github.dockerjava.api.model; + +public class VolumeBind { + private final String hostPath; + private final String containerPath; + + public VolumeBind(String hostPath, String containerPath){ + this.hostPath = hostPath; + this.containerPath = containerPath; + } + + public String getContainerPath() { + return containerPath; + } + + public String getHostPath() { + return hostPath; + } + + @Override + public String toString() { + return hostPath + ":" + containerPath; + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/VolumeBinds.java b/src/main/java/com/github/dockerjava/api/model/VolumeBinds.java new file mode 100644 index 00000000..e23ddc9e --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/VolumeBinds.java @@ -0,0 +1,70 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.core.JsonGenerator; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.NullNode; + +// This is not going to be serialized +@JsonDeserialize(using = VolumeBinds.Deserializer.class) +@JsonSerialize(using = VolumeBinds.Serializer.class) +public class VolumeBinds { + private final VolumeBind[] binds; + + public VolumeBinds(VolumeBind... binds) { + this.binds = binds; + } + + public VolumeBind[] getBinds() { + return binds; + } + + public static final class Serializer extends JsonSerializer { + + @Override + public void serialize(VolumeBinds value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { + jgen.writeStartObject(); + for (final VolumeBind bind : value.binds) { + jgen.writeStringField(bind.getContainerPath(), bind.getHostPath()); + } + jgen.writeEndObject(); + } + } + + public static final class Deserializer extends JsonDeserializer { + @Override + public VolumeBinds deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + + List binds = new ArrayList(); + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + for (Iterator> it = node.fields(); it.hasNext();) { + Map.Entry field = it.next(); + JsonNode value = field.getValue(); + if (!value.equals(NullNode.getInstance())) { + if (!value.isTextual()){ + throw deserializationContext.mappingException("Expected path for '"+field.getKey()+"'in host but got '"+ value+"'."); + } + VolumeBind bind = new VolumeBind(value.asText(),field.getKey()); + binds.add(bind); + } + } + return new VolumeBinds(binds.toArray(new VolumeBind[binds.size()])); + } + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/VolumeRW.java b/src/main/java/com/github/dockerjava/api/model/VolumeRW.java new file mode 100644 index 00000000..717385d9 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/VolumeRW.java @@ -0,0 +1,115 @@ +package com.github.dockerjava.api.model; + +import java.io.IOException; +import java.util.Map.Entry; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.NullNode; + +/** + * Represents a bind mounted volume in a Docker container. + * + * @see Bind + */ +@JsonDeserialize(using = VolumeRW.Deserializer.class) +@JsonSerialize(using = VolumeRW.Serializer.class) +public class VolumeRW { + + private Volume volume; + + private AccessMode accessMode = AccessMode.rw; + + public VolumeRW(Volume volume) { + this.volume = volume; + } + + public VolumeRW(Volume volume, AccessMode accessMode) { + this.volume = volume; + this.accessMode = accessMode; + } + + public Volume getVolume() { + return volume; + } + + public AccessMode getAccessMode() { + return accessMode; + } + + + /** + * Returns a string representation of this {@link VolumeRW} suitable + * for inclusion in a JSON message. + * The returned String is simply the container path, {@link #getPath()}. + * + * @return a string representation of this {@link VolumeRW} + */ + @Override + public String toString() { + return getVolume() + ":" + getAccessMode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof VolumeRW) { + VolumeRW other = (VolumeRW) obj; + return new EqualsBuilder().append(getVolume(), other.getVolume()).append(accessMode, other.getAccessMode()) + .isEquals(); + } else + return super.equals(obj); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(getVolume()).append(getAccessMode()).toHashCode(); + } + + public static class Serializer extends JsonSerializer { + + @Override + public void serialize(VolumeRW volumeRW, JsonGenerator jsonGen, + SerializerProvider serProvider) throws IOException, + JsonProcessingException { + + jsonGen.writeStartObject(); + jsonGen.writeFieldName(volumeRW.getVolume().getPath()); + jsonGen.writeString(Boolean.toString(volumeRW.getAccessMode().toBoolean())); + jsonGen.writeEndObject(); + } + + } + + public static class Deserializer extends JsonDeserializer { + @Override + public VolumeRW deserialize(JsonParser jsonParser, + DeserializationContext deserializationContext) + throws IOException, JsonProcessingException { + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + if (!node.equals(NullNode.getInstance())) { + Entry field = node.fields().next(); + String volume = field.getKey(); + AccessMode accessMode = AccessMode.fromBoolean(field.getValue().asBoolean()); + return new VolumeRW(new Volume(volume), accessMode); + } else { + return null; + } + } + } + + + +} diff --git a/src/main/java/com/github/dockerjava/api/model/Volumes.java b/src/main/java/com/github/dockerjava/api/model/Volumes.java new file mode 100644 index 00000000..b85536e1 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/Volumes.java @@ -0,0 +1,80 @@ +package com.github.dockerjava.api.model; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.NullNode; + + +@JsonSerialize(using = Volumes.Serializer.class) +@JsonDeserialize(using = Volumes.Deserializer.class) +public class Volumes { + + private Volume[] volumes; + + public Volumes(Volume... volumes) { + this.volumes = volumes; + } + + public Volumes(List volumes) { + this.volumes = volumes.toArray(new Volume[volumes.size()]); + } + + public Volume[] getVolumes() { + return volumes; + } + + public static class Serializer extends JsonSerializer { + + @Override + public void serialize(Volumes volumes, JsonGenerator jsonGen, + SerializerProvider serProvider) throws IOException, + JsonProcessingException { + + jsonGen.writeStartObject(); + for (Volume volume : volumes.getVolumes()) { + jsonGen.writeFieldName(volume.getPath()); + jsonGen.writeStartObject(); + jsonGen.writeEndObject(); + //jsonGen.writeString(Boolean.toString(volume.getAccessMode().equals(AccessMode.rw) ? true: false)); + } + jsonGen.writeEndObject(); + } + + } + + public static class Deserializer extends JsonDeserializer { + @Override + public Volumes deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + + List volumes = new ArrayList(); + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + for (Iterator> it = node.fields(); it.hasNext();) { + + Map.Entry field = it.next(); + if (!field.getValue().equals(NullNode.getInstance())) { + String path = field.getKey(); + Volume volume = new Volume(path); + volumes.add(volume); + } + } + return new Volumes(volumes.toArray(new Volume[0])); + } + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/VolumesFrom.java b/src/main/java/com/github/dockerjava/api/model/VolumesFrom.java new file mode 100644 index 00000000..9aae9f30 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/VolumesFrom.java @@ -0,0 +1,129 @@ +package com.github.dockerjava.api.model; + +import java.io.IOException; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +@JsonSerialize(using = VolumesFrom.Serializer.class) +@JsonDeserialize(using = VolumesFrom.Deserializer.class) +public class VolumesFrom { + + private String container; + + private AccessMode accessMode; + + public VolumesFrom(String container) { + this(container, AccessMode.DEFAULT); + } + + public VolumesFrom(String container, AccessMode accessMode) { + this.container = container; + this.accessMode = accessMode; + } + + public String getContainer() { + return container; + } + + public AccessMode getAccessMode() { + return accessMode; + } + + + /** + * Parses a volume from specification to a {@link VolumesFrom}. + * + * @param serialized the specification, e.g. container:ro + * @return a {@link VolumesFrom} matching the specification + * @throws IllegalArgumentException if the specification cannot be parsed + */ + public static VolumesFrom parse(String serialized) { + try { + String[] parts = serialized.split(":"); + switch (parts.length) { + case 1: { + return new VolumesFrom(parts[0]); + } + case 2: { + return new VolumesFrom(parts[0], AccessMode.valueOf(parts[1])); + } + + default: { + throw new IllegalArgumentException(); + } + } + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing Bind '" + serialized + + "'"); + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof VolumesFrom) { + VolumesFrom other = (VolumesFrom) obj; + return new EqualsBuilder().append(container, other.getContainer()) + .append(accessMode, other.getAccessMode()).isEquals(); + } else + return super.equals(obj); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(container) + .append(accessMode).toHashCode(); + } + + /** + * Returns a string representation of this {@link VolumesFrom} suitable + * for inclusion in a JSON message. + * The format is <container>:<access mode>, + * like the argument in {@link #parse(String)}. + * + * @return a string representation of this {@link VolumesFrom} + */ + @Override + public String toString() { + return container + ":" + accessMode.toString(); + } + + public static class Serializer extends JsonSerializer { + + @Override + public void serialize(VolumesFrom volumeFrom, JsonGenerator jsonGen, + SerializerProvider serProvider) throws IOException, + JsonProcessingException { + + jsonGen.writeString(volumeFrom.toString()); + + } + + } + + public static class Deserializer extends JsonDeserializer { + @Override + public VolumesFrom deserialize(JsonParser jsonParser, + DeserializationContext deserializationContext) + throws IOException, JsonProcessingException { + + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + return VolumesFrom.parse(node.asText()); + + } + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/VolumesRW.java b/src/main/java/com/github/dockerjava/api/model/VolumesRW.java new file mode 100644 index 00000000..a898ed57 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/VolumesRW.java @@ -0,0 +1,75 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.core.JsonGenerator; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.NullNode; + +// This is not going to be serialized +@JsonSerialize(using = VolumesRW.Serializer.class) +@JsonDeserialize(using = VolumesRW.Deserializer.class) +public class VolumesRW { + private final VolumeRW[] volumesRW; + + public VolumesRW(VolumeRW... binds) { + this.volumesRW = binds; + } + + public VolumeRW[] getVolumesRW() { + return volumesRW; + } + + public static final class Serializer extends JsonSerializer { + + @Override + public void serialize(VolumesRW value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { + jgen.writeStartObject(); + for (final VolumeRW volumeRW : value.volumesRW) { + jgen.writeBooleanField(volumeRW.getVolume().getPath(), volumeRW.getAccessMode().toBoolean()); + } + jgen.writeEndObject(); + } + + } + + public static final class Deserializer extends JsonDeserializer { + @Override + public VolumesRW deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + + List volumesRW = new ArrayList(); + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + + + for (Iterator> it = node.fields(); it.hasNext();) { + Map.Entry field = it.next(); + JsonNode value = field.getValue(); + + if (!value.equals(NullNode.getInstance())) { + if (!value.isBoolean()){ + throw deserializationContext.mappingException("Expected access mode for '"+field.getKey()+"' in host but got '"+ value+"'."); + } + + VolumeRW bind = new VolumeRW(new Volume(field.getKey()), AccessMode.fromBoolean(value.asBoolean())); + volumesRW.add(bind); + } + } + return new VolumesRW(volumesRW.toArray(new VolumeRW[volumesRW.size()])); + } + } + +} diff --git a/src/main/java/com/github/dockerjava/core/AuthConfigFile.java b/src/main/java/com/github/dockerjava/core/AuthConfigFile.java new file mode 100644 index 00000000..862259a8 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/AuthConfigFile.java @@ -0,0 +1,156 @@ +package com.github.dockerjava.core; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.github.dockerjava.api.model.AuthConfigurations; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.model.AuthConfig; + +public class AuthConfigFile { + private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final TypeReference> CONFIG_MAP_TYPE = new TypeReference>() {}; + private final Map authConfigMap; + + public AuthConfigFile() { + authConfigMap = new HashMap(); + } + + void addConfig(AuthConfig config) { + authConfigMap.put(config.getServerAddress(), config); + } + + public AuthConfig resolveAuthConfig(String hostname) { + if (StringUtils.isEmpty(hostname) || AuthConfig.DEFAULT_SERVER_ADDRESS.equals(hostname)) { + return authConfigMap.get(AuthConfig.DEFAULT_SERVER_ADDRESS); + } + AuthConfig c = authConfigMap.get(hostname); + if (c != null) { + return c; + } + + // Maybe they have a legacy config file, we will iterate the keys converting + // them to the new format and testing + String normalizedHostname = convertToHostname(hostname); + for (Map.Entry entry : authConfigMap.entrySet()) { + String registry = entry.getKey(); + AuthConfig config = entry.getValue(); + if (convertToHostname(registry).equals(normalizedHostname)) { + return config; + } + } + return null; + } + + public AuthConfigurations getAuthConfigurations() { + final AuthConfigurations authConfigurations = new AuthConfigurations(); + for(Map.Entry authConfigEntry : authConfigMap.entrySet()) { + authConfigurations.addConfig(authConfigEntry.getValue()); + } + + return authConfigurations; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((authConfigMap == null) ? 0 : authConfigMap.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AuthConfigFile other = (AuthConfigFile) obj; + if (authConfigMap == null) { + if (other.authConfigMap != null) + return false; + } else if (!authConfigMap.equals(other.authConfigMap)) + return false; + return true; + } + + @Override + public String toString() { + return "AuthConfigFile [authConfigMap=" + authConfigMap + "]"; + } + + + public static AuthConfigFile loadConfig(File confFile) throws IOException { + AuthConfigFile configFile = new AuthConfigFile(); + if (!confFile.exists()) { + return new AuthConfigFile(); + } + Map configMap = null; + try { + configMap = MAPPER.readValue(confFile, CONFIG_MAP_TYPE); + } catch (IOException e) { + // pass + } + if (configMap != null) { + for (Map.Entry entry : configMap.entrySet()) { + AuthConfig authConfig = entry.getValue(); + decodeAuth(authConfig.getAuth(), authConfig); + authConfig.setAuth(null); + authConfig.setServerAddress(entry.getKey()); + configFile.addConfig(authConfig); + } + } else { + List authFileContent = FileUtils.readLines(confFile); + if (authFileContent.size() < 2) { + throw new IOException("The Auth Config file is empty"); + } + AuthConfig config = new AuthConfig(); + String[] origAuth = authFileContent.get(0).split(" = "); + if (origAuth.length != 2) { + throw new IOException("Invalid Auth config file"); + } + decodeAuth(origAuth[1], config); + + String[] origEmail = authFileContent.get(1).split(" = "); + if (origEmail.length != 2) { + throw new IOException("Invalid Auth config file"); + } + config.setEmail(origEmail[1]); + configFile.addConfig(config); + } + return configFile; + + } + + static void decodeAuth(String auth, AuthConfig config) throws IOException { + String str = new String(Base64.decodeBase64(auth), Charset.forName("UTF-8")); + String[] parts = str.split(":", 2); + if (parts.length != 2) { + throw new IOException("Invalid auth configuration file"); + } + config.setUsername(parts[0]); + config.setPassword(parts[1]); + } + + static String convertToHostname(String server) { + String stripped = server; + if (server.startsWith("http://")) { + stripped = server.substring(7); + } else if (server.startsWith("https://")) { + stripped = server.substring(8); + } + String[] numParts = stripped.split("/", 2); + return numParts[0]; + } +} diff --git a/src/main/java/com/github/dockerjava/core/CertificateUtils.java b/src/main/java/com/github/dockerjava/core/CertificateUtils.java new file mode 100644 index 00000000..6b19a201 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/CertificateUtils.java @@ -0,0 +1,141 @@ +package com.github.dockerjava.core; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.apache.commons.io.IOUtils; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; + +public class CertificateUtils { + + public static boolean verifyCertificatesExist(String dockerCertPath) { + String[] files = {"ca.pem", "cert.pem", "key.pem"}; + for (String file : files) { + File path = new File(dockerCertPath, file); + boolean exists = path.exists(); + if(!exists) { + return false; + } + } + + return true; + } + + public static KeyStore createKeyStore(final String dockerCertPath) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, CertificateException, KeyStoreException { + KeyPair keyPair = loadPrivateKey(dockerCertPath); + Certificate privateCertificate = loadCertificate(dockerCertPath); + + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null); + + keyStore.setKeyEntry("docker", keyPair.getPrivate(), "docker".toCharArray(), new Certificate[]{privateCertificate}); + return keyStore; + } + + public static KeyStore createTrustStore(final String dockerCertPath) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException { + File caPath = new File(dockerCertPath, "ca.pem"); + BufferedReader reader = new BufferedReader(new FileReader(caPath)); + PEMParser pemParser = null; + + try { + pemParser = new PEMParser(reader); + X509CertificateHolder certificateHolder = (X509CertificateHolder) pemParser.readObject(); + Certificate caCertificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder); + + KeyStore trustStore = KeyStore.getInstance("JKS"); + trustStore.load(null); + trustStore.setCertificateEntry("ca", caCertificate); + return trustStore; + + } + finally { + if(pemParser != null) { + IOUtils.closeQuietly(pemParser); + } + + if(reader != null) { + IOUtils.closeQuietly(reader); + } + } + + } + + private static Certificate loadCertificate(final String dockerCertPath) throws IOException, CertificateException { + File certificate = new File(dockerCertPath, "cert.pem"); + BufferedReader reader = new BufferedReader(new FileReader(certificate)); + PEMParser pemParser = null; + + try { + pemParser = new PEMParser(reader); + X509CertificateHolder certificateHolder = (X509CertificateHolder) pemParser.readObject(); + return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder); + } + finally { + if(pemParser != null) { + IOUtils.closeQuietly(pemParser); + } + + if(reader != null) { + IOUtils.closeQuietly(reader); + } + } + + } + + private static KeyPair loadPrivateKey(final String dockerCertPath) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { + File certificate = new File(dockerCertPath, "key.pem"); + BufferedReader reader = new BufferedReader(new FileReader(certificate)); + + PEMParser pemParser = null; + + try { + pemParser = new PEMParser(reader); + + PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject(); + + byte[] pemPrivateKeyEncoded = pemKeyPair.getPrivateKeyInfo().getEncoded(); + byte[] pemPublicKeyEncoded = pemKeyPair.getPublicKeyInfo().getEncoded(); + + KeyFactory factory = KeyFactory.getInstance("RSA"); + + X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pemPublicKeyEncoded); + PublicKey publicKey = factory.generatePublic(publicKeySpec); + + PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pemPrivateKeyEncoded); + PrivateKey privateKey = factory.generatePrivate(privateKeySpec); + + return new KeyPair(publicKey, privateKey); + + } + finally { + if(pemParser != null) { + IOUtils.closeQuietly(pemParser); + } + + if(reader != null) { + IOUtils.closeQuietly(reader); + } + } + + + } + +} diff --git a/src/main/java/com/kpelykh/docker/client/utils/CompressArchiveUtil.java b/src/main/java/com/github/dockerjava/core/CompressArchiveUtil.java similarity index 78% rename from src/main/java/com/kpelykh/docker/client/utils/CompressArchiveUtil.java rename to src/main/java/com/github/dockerjava/core/CompressArchiveUtil.java index aabf4cbb..297adc76 100644 --- a/src/main/java/com/kpelykh/docker/client/utils/CompressArchiveUtil.java +++ b/src/main/java/com/github/dockerjava/core/CompressArchiveUtil.java @@ -1,4 +1,4 @@ -package com.kpelykh.docker.client.utils; +package com.github.dockerjava.core; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; @@ -6,6 +6,8 @@ import java.io.*; +import static com.github.dockerjava.core.FilePathUtil.relativize; + public class CompressArchiveUtil { public static File archiveTARFiles(File base, Iterable files, String archiveNameWithOutExtension) throws IOException { @@ -17,6 +19,12 @@ public static File archiveTARFiles(File base, Iterable files, String archi TarArchiveEntry tarEntry = new TarArchiveEntry(file); tarEntry.setName(relativize(base, file)); + if (!file.isDirectory()) { + if (file.canExecute()) { + tarEntry.setMode(tarEntry.getMode() | 0755); + } + } + tos.putArchiveEntry(tarEntry); if (!file.isDirectory()) { @@ -30,9 +38,4 @@ public static File archiveTARFiles(File base, Iterable files, String archi return tarFile; } - - private static String relativize(File base, File absolute) { - String relative = base.toURI().relativize(absolute.toURI()).getPath(); - return relative; - } } diff --git a/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java b/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java new file mode 100644 index 00000000..4aa7d5ac --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java @@ -0,0 +1,71 @@ +package com.github.dockerjava.core; + +import java.util.ServiceLoader; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.DockerCmdExecFactory; +import com.github.dockerjava.core.DockerClientConfig.DockerClientConfigBuilder; + +public class DockerClientBuilder { + + private static ServiceLoader serviceLoader = ServiceLoader.load(DockerCmdExecFactory.class); + + private DockerClientImpl dockerClient = null; + private DockerCmdExecFactory dockerCmdExecFactory = null; + + private DockerClientBuilder(DockerClientImpl dockerClient) { + this.dockerClient = dockerClient; + } + + public static DockerClientBuilder getInstance() { + return new DockerClientBuilder(DockerClientImpl.getInstance()); + } + + public static DockerClientBuilder getInstance(DockerClientConfigBuilder dockerClientConfigBuilder) { + return getInstance(dockerClientConfigBuilder.build()); + } + + public static DockerClientBuilder getInstance(DockerClientConfig dockerClientConfig) { + return new DockerClientBuilder(DockerClientImpl + .getInstance(dockerClientConfig)); + } + + public static DockerClientBuilder getInstance(String serverUrl) { + return new DockerClientBuilder(DockerClientImpl + .getInstance(serverUrl)); + } + + public static DockerCmdExecFactory getDefaultDockerCmdExecFactory() { + // clearing the cache is needed because otherwise we will get + // the same DockerCmdExecFactory instance each time + serviceLoader.reload(); + if(!serviceLoader.iterator().hasNext()) { + throw new RuntimeException("Fatal: Can't find any implementation of '" + DockerCmdExecFactory.class.getName() + "' in the current classpath."); + } + + return serviceLoader.iterator().next(); + } + + public DockerClientBuilder withDockerCmdExecFactory( + DockerCmdExecFactory dockerCmdExecFactory) { + this.dockerCmdExecFactory = dockerCmdExecFactory; + return this; + } + + public DockerClientBuilder withServiceLoaderClassLoader(ClassLoader classLoader) + { + serviceLoader = ServiceLoader.load(DockerCmdExecFactory.class, classLoader); + return this; + } + + public DockerClient build() { + if(dockerCmdExecFactory != null) { + dockerClient.withDockerCmdExecFactory(dockerCmdExecFactory); + } + else { + dockerClient.withDockerCmdExecFactory(getDefaultDockerCmdExecFactory()); + } + + return dockerClient; + } +} diff --git a/src/main/java/com/github/dockerjava/core/DockerClientConfig.java b/src/main/java/com/github/dockerjava/core/DockerClientConfig.java new file mode 100644 index 00000000..7e0b57b0 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/DockerClientConfig.java @@ -0,0 +1,518 @@ +package com.github.dockerjava.core; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.Serializable; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import com.github.dockerjava.api.DockerClientException; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.AuthConfigurations; +import com.github.dockerjava.core.NameParser.HostnameReposName; +import com.github.dockerjava.core.NameParser.ReposTag; + +public class DockerClientConfig implements Serializable { + + private static final long serialVersionUID = -4307357472441531489L; + + private static final String DOCKER_HOST_PROPERTY = "DOCKER_HOST"; + private static final String DOCKER_CERT_PATH_PROPERTY = "DOCKER_CERT_PATH"; + private static final String DOCKER_VERIFY_TLS_PROPERTY = "DOCKER_TLS_VERIFY"; + private static final String DOCKER_IO_URL_PROPERTY = "docker.io.url"; + private static final String DOCKER_IO_VERSION_PROPERTY = "docker.io.version"; + private static final String DOCKER_IO_USERNAME_PROPERTY = "docker.io.username"; + private static final String DOCKER_IO_PASSWORD_PROPERTY = "docker.io.password"; + private static final String DOCKER_IO_EMAIL_PROPERTY = "docker.io.email"; + private static final String DOCKER_IO_SERVER_ADDRESS_PROPERTY = "docker.io.serverAddress"; + private static final String DOCKER_IO_READ_TIMEOUT_PROPERTY = "docker.io.readTimeout"; + // this is really confusing, as there are two ways to spell it + private static final String DOCKER_IO_ENABLE_LOGGING_FILTER_PROPERTY = "docker.io.enableLoggingFilter"; + private static final String DOCKER_IO_FOLLOW_REDIRECTS_FILTER_PROPERTY = "docker.io.followRedirectsFilter"; + private static final String DOCKER_IO_DOCKER_CERT_PATH_PROPERTY = "docker.io.dockerCertPath"; + private static final String DOCKER_IO_DOCKER_CFG_PATH_PROPERTY = "docker.io.dockerCfgPath"; + // connection pooling properties + private static final String DOCKER_IO_MAX_PER_ROUTE_PROPERTY = "docker.io.perRouteConnections"; + private static final String DOCKER_IO_MAX_TOTAL_PROPERTY = "docker.io.totalConnections"; + /** + * A map from the environment name to the interval name. + */ + // Immutable ish + private static final Map ENV_NAME_TO_IO_NAME; + static { + Map m = new HashMap(); + m.put("DOCKER_URL", DOCKER_IO_URL_PROPERTY); + m.put("DOCKER_VERSION", DOCKER_IO_VERSION_PROPERTY); + m.put("DOCKER_USERNAME", DOCKER_IO_USERNAME_PROPERTY); + m.put("DOCKER_PASSWORD", DOCKER_IO_PASSWORD_PROPERTY); + m.put("DOCKER_EMAIL", DOCKER_IO_EMAIL_PROPERTY); + m.put("DOCKER_SERVER_ADDRESS", DOCKER_IO_SERVER_ADDRESS_PROPERTY); + m.put("DOCKER_READ_TIMEOUT", DOCKER_IO_READ_TIMEOUT_PROPERTY); + m.put("DOCKER_LOGGING_FILTER_ENABLED", DOCKER_IO_ENABLE_LOGGING_FILTER_PROPERTY); + m.put("DOCKER_FOLLOW_REDIRECTS_FILTER_ENABLED", DOCKER_IO_FOLLOW_REDIRECTS_FILTER_PROPERTY); + m.put(DOCKER_CERT_PATH_PROPERTY, DOCKER_IO_DOCKER_CERT_PATH_PROPERTY); + m.put("DOCKER_CFG_PATH", DOCKER_IO_DOCKER_CFG_PATH_PROPERTY); + ENV_NAME_TO_IO_NAME = Collections.unmodifiableMap(m); + } + + private static final String DOCKER_IO_PROPERTIES_PROPERTY = "docker.io.properties"; + private URI uri; + private final String version, username, password, email, serverAddress, dockerCfgPath; + private final Integer readTimeout; + private final boolean loggingFilterEnabled; + private final boolean followRedirectsFilterEnabled; + private final SSLConfig sslConfig; + + private final Integer maxTotalConnections; + private final Integer maxPerRouteConnections; + + DockerClientConfig(URI uri, String version, String username, String password, String email, String serverAddress, + String dockerCfgPath, Integer readTimeout, boolean loggingFilterEnabled, boolean followRedirectsFilterEnabled, + SSLConfig sslConfig, Integer maxTotalConns, Integer maxPerRouteConns) { + this.uri = uri; + this.version = version; + this.username = username; + this.password = password; + this.email = email; + this.serverAddress = serverAddress; + this.dockerCfgPath = dockerCfgPath; + this.readTimeout = readTimeout; + this.loggingFilterEnabled = loggingFilterEnabled; + this.followRedirectsFilterEnabled = followRedirectsFilterEnabled; + this.sslConfig = sslConfig; + this.maxTotalConnections = maxTotalConns; + this.maxPerRouteConnections = maxPerRouteConns; + } + + private static Properties loadIncludedDockerProperties(Properties systemProperties) { + try { + Properties p = new Properties(); + p.load(DockerClientConfig.class.getResourceAsStream("/" + DOCKER_IO_PROPERTIES_PROPERTY)); + replaceProperties(p, systemProperties); + return p; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static void replaceProperties(Properties properties, Properties replacements) { + for (Object objectKey : properties.keySet()) { + String key = objectKey.toString(); + properties.setProperty(key, replaceProperties(properties.getProperty(key), replacements)); + } + } + + private static String replaceProperties(String s, Properties replacements) { + for (Map.Entry entry : replacements.entrySet()) { + String key = "${" + entry.getKey() + "}"; + while (s.contains(key)) { + s = s.replace(key, String.valueOf(entry.getValue())); + } + } + return s; + } + + /** + * Creates a new Properties object containing values overridden from ${user.home}/.docker.io.properties + * + * @param p The original set of properties to override + * @return A copy of the original Properties with overridden values + */ + private static Properties overrideDockerPropertiesWithSettingsFromUserHome(Properties p, Properties systemProperties) { + Properties overriddenProperties = new Properties(); + overriddenProperties.putAll(p); + + final File usersDockerPropertiesFile = new File(systemProperties.getProperty("user.home"), "." + DOCKER_IO_PROPERTIES_PROPERTY); + if (usersDockerPropertiesFile.isFile()) { + try { + final FileInputStream in = new FileInputStream(usersDockerPropertiesFile); + try { + overriddenProperties.load(in); + } finally { + in.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return overriddenProperties; + } + + private static Properties overrideDockerPropertiesWithEnv(Properties properties, Map env) { + Properties overriddenProperties = new Properties(); + overriddenProperties.putAll(properties); + + // special case which is a sensible default + if (env.containsKey(DOCKER_HOST_PROPERTY)) { + overriddenProperties.setProperty(DOCKER_IO_URL_PROPERTY, env.get(DOCKER_HOST_PROPERTY).replace("tcp", protocol(env))); + } + + for (Map.Entry envEntry : env.entrySet()) { + String envKey = envEntry.getKey(); + if (ENV_NAME_TO_IO_NAME.containsKey(envKey)) { + overriddenProperties.setProperty(ENV_NAME_TO_IO_NAME.get(envKey), envEntry.getValue()); + } + } + + return overriddenProperties; + } + + private static String protocol(Map env) { + // if this is set, we assume we need SSL + return env.containsKey(DOCKER_CERT_PATH_PROPERTY) || "1".equals(env.get(DOCKER_VERIFY_TLS_PROPERTY)) ? "https" : "http"; + } + + /** + * Creates a new Properties object containing values overridden from the System properties + * + * @param p The original set of properties to override + * @return A copy of the original Properties with overridden values + */ + private static Properties overrideDockerPropertiesWithSystemProperties(Properties p, Properties systemProperties) { + Properties overriddenProperties = new Properties(); + overriddenProperties.putAll(p); + + for (String key : new String[]{ + DOCKER_IO_URL_PROPERTY, + DOCKER_IO_VERSION_PROPERTY, + DOCKER_IO_USERNAME_PROPERTY, + DOCKER_IO_PASSWORD_PROPERTY, + DOCKER_IO_EMAIL_PROPERTY, + DOCKER_IO_SERVER_ADDRESS_PROPERTY, + DOCKER_IO_READ_TIMEOUT_PROPERTY, + DOCKER_IO_ENABLE_LOGGING_FILTER_PROPERTY, + DOCKER_IO_FOLLOW_REDIRECTS_FILTER_PROPERTY, + DOCKER_IO_DOCKER_CERT_PATH_PROPERTY, + DOCKER_IO_DOCKER_CFG_PATH_PROPERTY, + }) { + if (systemProperties.containsKey(key)) { + overriddenProperties.setProperty(key, systemProperties.getProperty(key)); + } + } + return overriddenProperties; + } + + public static DockerClientConfigBuilder createDefaultConfigBuilder() { + return createDefaultConfigBuilder(System.getenv(), System.getProperties()); + } + + /** + * Allows you to build the config without system environment interfering for more robust testing + */ + static DockerClientConfigBuilder createDefaultConfigBuilder(Map env, Properties systemProperties) { + Properties properties = loadIncludedDockerProperties(systemProperties); + properties = overrideDockerPropertiesWithSettingsFromUserHome(properties, systemProperties); + properties = overrideDockerPropertiesWithEnv(properties, env); + properties = overrideDockerPropertiesWithSystemProperties(properties, systemProperties); + return new DockerClientConfigBuilder().withProperties(properties); + } + + public URI getUri() { + return uri; + } + + public void setUri(URI uri) { + this.uri = uri; + } + + public String getVersion() { + return version; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getEmail() { + return email; + } + + public String getServerAddress() { + return serverAddress; + } + + public Integer getReadTimeout() { + return readTimeout; + } + + public boolean isLoggingFilterEnabled() { + return loggingFilterEnabled; + } + + public boolean followRedirectsFilterEnabled() { + return followRedirectsFilterEnabled; + } + + public SSLConfig getSslConfig() { + return sslConfig; + } + + public String getDockerCfgPath() { + return dockerCfgPath; + } + + public Integer getMaxTotalConnections() { + return maxTotalConnections; + } + + public Integer getMaxPerRoutConnections() { + return maxPerRouteConnections; + } + + private AuthConfig getAuthConfig() { + AuthConfig authConfig = null; + if (getUsername() != null && getPassword() != null && getEmail() != null + && getServerAddress() != null) { + authConfig = new AuthConfig(); + authConfig.setUsername(getUsername()); + authConfig.setPassword(getPassword()); + authConfig.setEmail(getEmail()); + authConfig.setServerAddress(getServerAddress()); + } + return authConfig; + } + + public AuthConfig effectiveAuthConfig(String imageName) { + AuthConfig authConfig = null; + + String dockerCfgFile = getDockerCfgPath(); + + if (dockerCfgFile != null && imageName != null) { + AuthConfigFile authConfigFile; + try { + authConfigFile = AuthConfigFile.loadConfig(new File( + dockerCfgFile)); + } catch (IOException e) { + throw new DockerClientException( + "Failed to parse dockerCfgFile", e); + } + ReposTag reposTag = NameParser.parseRepositoryTag(imageName); + HostnameReposName hostnameReposName = NameParser + .resolveRepositoryName(reposTag.repos); + + authConfig = authConfigFile + .resolveAuthConfig(hostnameReposName.hostname); + } + + AuthConfig _authConfig = getAuthConfig(); + + if(_authConfig != null) authConfig = _authConfig; + + return authConfig; + } + + public AuthConfigurations getAuthConfigurations() { + String dockerCfgFile = getDockerCfgPath(); + if (dockerCfgFile != null) { + AuthConfigFile authConfigFile; + try { + authConfigFile = AuthConfigFile.loadConfig(new File( + dockerCfgFile)); + } catch (IOException e) { + throw new DockerClientException( + "Failed to parse dockerCfgFile", e); + } + + return authConfigFile.getAuthConfigurations(); + } + + return new AuthConfigurations(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DockerClientConfig that = (DockerClientConfig) o; + + if (loggingFilterEnabled != that.loggingFilterEnabled) return false; + if (sslConfig != null ? !sslConfig.equals(that.sslConfig) : that.sslConfig != null) + return false; + if (dockerCfgPath != null ? !dockerCfgPath.equals(that.dockerCfgPath) : that.dockerCfgPath != null) + return false; + if (email != null ? !email.equals(that.email) : that.email != null) return false; + if (password != null ? !password.equals(that.password) : that.password != null) return false; + if (readTimeout != null ? !readTimeout.equals(that.readTimeout) : that.readTimeout != null) return false; + if (serverAddress != null ? !serverAddress.equals(that.serverAddress) : that.serverAddress != null) + return false; + if (uri != null ? !uri.equals(that.uri) : that.uri != null) return false; + if (username != null ? !username.equals(that.username) : that.username != null) return false; + if (version != null ? !version.equals(that.version) : that.version != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = uri != null ? uri.hashCode() : 0; + result = 31 * result + (version != null ? version.hashCode() : 0); + result = 31 * result + (username != null ? username.hashCode() : 0); + result = 31 * result + (password != null ? password.hashCode() : 0); + result = 31 * result + (email != null ? email.hashCode() : 0); + result = 31 * result + (serverAddress != null ? serverAddress.hashCode() : 0); + result = 31 * result + (dockerCfgPath != null ? dockerCfgPath.hashCode() : 0); + result = 31 * result + (sslConfig != null ? sslConfig.hashCode() : 0); + result = 31 * result + (readTimeout != null ? readTimeout.hashCode() : 0); + result = 31 * result + (loggingFilterEnabled ? 1 : 0); + return result; + } + + @Override + public String toString() { + return "DockerClientConfig{" + + "uri=" + uri + + ", version='" + version + '\'' + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + ", email='" + email + '\'' + + ", serverAddress='" + serverAddress + '\'' + + ", dockerCfgPath='" + dockerCfgPath + '\'' + + ", sslConfig='" + sslConfig + '\'' + + ", readTimeout=" + readTimeout + + ", loggingFilterEnabled=" + loggingFilterEnabled + + ", followRedirectsFilterEnabled=" + followRedirectsFilterEnabled + + '}'; + } + + public static class DockerClientConfigBuilder { + private URI uri; + private String version, username, password, email, serverAddress, dockerCfgPath; + private Integer readTimeout, maxTotalConnections, maxPerRouteConnections; + private boolean loggingFilterEnabled, followRedirectsFilterEnabled; + private SSLConfig sslConfig; + + /** + * This will set all fields in the builder to those contained in the Properties object. The Properties object + * should contain the following docker.io.* keys: url, version, username, password, email, dockerCertPath, and + * dockerCfgPath. If docker.io.readTimeout or docker.io.enableLoggingFilter are not contained, they will be set + * to 1000 and true, respectively. + */ + public DockerClientConfigBuilder withProperties(Properties p) { + return withUri(p.getProperty(DOCKER_IO_URL_PROPERTY)) + .withVersion(p.getProperty(DOCKER_IO_VERSION_PROPERTY)) + .withUsername(p.getProperty(DOCKER_IO_USERNAME_PROPERTY)) + .withPassword(p.getProperty(DOCKER_IO_PASSWORD_PROPERTY)) + .withEmail(p.getProperty(DOCKER_IO_EMAIL_PROPERTY)) + .withServerAddress(p.getProperty(DOCKER_IO_SERVER_ADDRESS_PROPERTY)) + .withReadTimeout(Integer.valueOf(p.getProperty(DOCKER_IO_READ_TIMEOUT_PROPERTY, "0"))) + .withLoggingFilter(Boolean.valueOf(p.getProperty(DOCKER_IO_ENABLE_LOGGING_FILTER_PROPERTY, "true"))) + .withFollowRedirectsFilter(Boolean.valueOf(p.getProperty(DOCKER_IO_FOLLOW_REDIRECTS_FILTER_PROPERTY, "false"))) + .withDockerCertPath(p.getProperty(DOCKER_IO_DOCKER_CERT_PATH_PROPERTY)) + .withDockerCfgPath(p.getProperty(DOCKER_IO_DOCKER_CFG_PATH_PROPERTY)) + .withMaxPerRouteConnections(integerValue(p.getProperty(DOCKER_IO_MAX_PER_ROUTE_PROPERTY))) + .withMaxTotalConnections(integerValue(p.getProperty(DOCKER_IO_MAX_TOTAL_PROPERTY))); + } + + private Integer integerValue(String value) { + if(value != null) + return Integer.valueOf(value); + else + return null; + } + + public final DockerClientConfigBuilder withUri(String uri) { + checkNotNull(uri, "uri was not specified"); + this.uri = URI.create(uri); + return this; + } + + public final DockerClientConfigBuilder withVersion(String version) { + this.version = version; + return this; + } + + public final DockerClientConfigBuilder withUsername(String username) { + this.username = username; + return this; + } + + public final DockerClientConfigBuilder withPassword(String password) { + this.password = password; + return this; + } + + public final DockerClientConfigBuilder withEmail(String email) { + this.email = email; + return this; + } + + public DockerClientConfigBuilder withServerAddress(String serverAddress) { + this.serverAddress = serverAddress; + return this; + } + + public final DockerClientConfigBuilder withReadTimeout(Integer readTimeout) { + this.readTimeout = readTimeout; + return this; + } + + public final DockerClientConfigBuilder withMaxTotalConnections(Integer maxTotalConnections) { + this.maxTotalConnections = maxTotalConnections; + return this; + } + + public final DockerClientConfigBuilder withMaxPerRouteConnections(Integer maxPerRouteConnections) { + this.maxPerRouteConnections = maxPerRouteConnections; + return this; + } + + public final DockerClientConfigBuilder withLoggingFilter(boolean loggingFilterEnabled) { + this.loggingFilterEnabled = loggingFilterEnabled; + return this; + } + + public final DockerClientConfigBuilder withFollowRedirectsFilter(boolean followRedirectsFilterEnabled) { + this.followRedirectsFilterEnabled = followRedirectsFilterEnabled; + return this; + } + + public final DockerClientConfigBuilder withDockerCertPath(String dockerCertPath) { + if(dockerCertPath != null) { + this.sslConfig = new LocalDirectorySSLConfig(dockerCertPath); + } + return this; + } + + public final DockerClientConfigBuilder withDockerCfgPath(String dockerCfgPath) { + this.dockerCfgPath = dockerCfgPath; + return this; + } + + + public final DockerClientConfigBuilder withSSLConfig(SSLConfig config) { + this.sslConfig = config; + return this; + } + + public DockerClientConfig build() { + return new DockerClientConfig( + uri, + version, + username, + password, + email, + serverAddress, + dockerCfgPath, + readTimeout, + loggingFilterEnabled, + followRedirectsFilterEnabled, + sslConfig, + maxTotalConnections, + maxPerRouteConnections + ); + } + } + +// +} diff --git a/src/main/java/com/github/dockerjava/core/DockerClientImpl.java b/src/main/java/com/github/dockerjava/core/DockerClientImpl.java new file mode 100644 index 00000000..c4b8aef1 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/DockerClientImpl.java @@ -0,0 +1,325 @@ +package com.github.dockerjava.core; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.*; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.AuthConfigurations; +import com.github.dockerjava.api.model.Identifier; +import com.github.dockerjava.core.command.*; + +/** + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + * @see "https://github.com/docker/docker/blob/master/api/client/commands.go" + */ +public class DockerClientImpl implements Closeable, DockerClient { + + private final DockerClientConfig dockerClientConfig; + + private DockerCmdExecFactory dockerCmdExecFactory; + + private DockerClientImpl() { + this(DockerClientConfig.createDefaultConfigBuilder().build()); + } + + private DockerClientImpl(String serverUrl) { + this(configWithServerUrl(serverUrl)); + } + + private DockerClientImpl(DockerClientConfig dockerClientConfig) { + checkNotNull(dockerClientConfig, + "config was not specified"); + this.dockerClientConfig = dockerClientConfig; + } + + private static DockerClientConfig configWithServerUrl(String serverUrl) { + return DockerClientConfig.createDefaultConfigBuilder() + .withUri(serverUrl).build(); + } + + public static DockerClientImpl getInstance() { + return new DockerClientImpl(); + } + + public static DockerClientImpl getInstance( + DockerClientConfig dockerClientConfig) { + return new DockerClientImpl(dockerClientConfig); + } + + public static DockerClientImpl getInstance(String serverUrl) { + return new DockerClientImpl(serverUrl); + } + + public DockerClientImpl withDockerCmdExecFactory( + DockerCmdExecFactory dockerCmdExecFactory) { + checkNotNull(dockerCmdExecFactory, + "dockerCmdExecFactory was not specified"); + this.dockerCmdExecFactory = dockerCmdExecFactory; + this.dockerCmdExecFactory.init(dockerClientConfig); + return this; + } + + private DockerCmdExecFactory getDockerCmdExecFactory() { + checkNotNull(dockerCmdExecFactory, + "dockerCmdExecFactory was not specified"); + return dockerCmdExecFactory; + } + + public AuthConfig authConfig() { + checkNotNull(dockerClientConfig.getUsername(), + "Configured username is null."); + checkNotNull(dockerClientConfig.getServerAddress(), + "Configured serverAddress is null."); + + AuthConfig authConfig = new AuthConfig(); + authConfig.setUsername(dockerClientConfig.getUsername()); + authConfig.setPassword(dockerClientConfig.getPassword()); + authConfig.setEmail(dockerClientConfig.getEmail()); + authConfig.setServerAddress(dockerClientConfig.getServerAddress()); + + return authConfig; + } + + /** + * * MISC API * + */ + + /** + * Authenticate with the server, useful for checking authentication. + */ + public AuthCmd authCmd() { + return new AuthCmdImpl(getDockerCmdExecFactory().createAuthCmdExec(), + authConfig()); + } + + public InfoCmd infoCmd() { + return new InfoCmdImpl(getDockerCmdExecFactory().createInfoCmdExec()); + } + + + + public PingCmd pingCmd() { + return new PingCmdImpl(getDockerCmdExecFactory().createPingCmdExec()); + } + + public VersionCmd versionCmd() { + return new VersionCmdImpl(getDockerCmdExecFactory() + .createVersionCmdExec()); + } + + /** + * * IMAGE API * + */ + public PullImageCmd pullImageCmd(String repository) { + return new PullImageCmdImpl(getDockerCmdExecFactory() + .createPullImageCmdExec(), dockerClientConfig.effectiveAuthConfig(repository), repository); + } + + public PushImageCmd pushImageCmd(String name) { + PushImageCmd cmd = new PushImageCmdImpl(getDockerCmdExecFactory() + .createPushImageCmdExec(), name); + + AuthConfig cfg = dockerClientConfig.effectiveAuthConfig(name); + if( cfg != null ) + cmd.withAuthConfig(cfg); + return cmd; + } + + public PushImageCmd pushImageCmd(Identifier identifier) { + PushImageCmd cmd = pushImageCmd(identifier.repository.name); + if( identifier.tag.isPresent() ) + cmd.withTag(identifier.tag.get()); + + AuthConfig cfg = dockerClientConfig.effectiveAuthConfig(identifier.repository.name); + if( cfg != null ) + cmd.withAuthConfig(cfg); + + return cmd; + } + + public SaveImageCmd saveImageCmd(String name) { + return new SaveImageCmdImpl(getDockerCmdExecFactory().createSaveImageCmdExec(), name); + } + + public CreateImageCmd createImageCmd(String repository, + InputStream imageStream) { + return new CreateImageCmdImpl(getDockerCmdExecFactory() + .createCreateImageCmdExec(), repository, imageStream); + } + + public SearchImagesCmd searchImagesCmd(String term) { + return new SearchImagesCmdImpl(getDockerCmdExecFactory() + .createSearchImagesCmdExec(), term); + } + + public RemoveImageCmd removeImageCmd(String imageId) { + return new RemoveImageCmdImpl(getDockerCmdExecFactory() + .createRemoveImageCmdExec(), imageId); + } + + public ListImagesCmd listImagesCmd() { + return new ListImagesCmdImpl(getDockerCmdExecFactory() + .createListImagesCmdExec()); + } + + public InspectImageCmd inspectImageCmd(String imageId) { + return new InspectImageCmdImpl(getDockerCmdExecFactory() + .createInspectImageCmdExec(), imageId); + } + + /** + * * CONTAINER API * + */ + public ListContainersCmd listContainersCmd() { + return new ListContainersCmdImpl(getDockerCmdExecFactory() + .createListContainersCmdExec()); + } + + public CreateContainerCmd createContainerCmd(String image) { + return new CreateContainerCmdImpl(getDockerCmdExecFactory() + .createCreateContainerCmdExec(), image); + } + + public StartContainerCmd startContainerCmd(String containerId) { + return new StartContainerCmdImpl(getDockerCmdExecFactory() + .createStartContainerCmdExec(), containerId); + } + + public InspectContainerCmd inspectContainerCmd(String containerId) { + return new InspectContainerCmdImpl(getDockerCmdExecFactory() + .createInspectContainerCmdExec(), containerId); + } + + public ExecCreateCmd execCreateCmd(String containerId) { + return new ExecCreateCmdImpl(getDockerCmdExecFactory().createExecCmdExec(), containerId); + } + + public RemoveContainerCmd removeContainerCmd(String containerId) { + return new RemoveContainerCmdImpl(getDockerCmdExecFactory() + .createRemoveContainerCmdExec(), containerId); + } + + public WaitContainerCmd waitContainerCmd(String containerId) { + return new WaitContainerCmdImpl(getDockerCmdExecFactory() + .createWaitContainerCmdExec(), containerId); + } + + public AttachContainerCmd attachContainerCmd(String containerId) { + return new AttachContainerCmdImpl(getDockerCmdExecFactory() + .createAttachContainerCmdExec(), containerId); + } + + public ExecStartCmd execStartCmd(String containerId) { + return new ExecStartCmdImpl(getDockerCmdExecFactory().createExecStartCmdExec(), containerId); + } + + public InspectExecCmd inspectExecCmd(String execId) { + return new InspectExecCmdImpl(getDockerCmdExecFactory().createInspectExecCmdExec(), execId); + } + + public LogContainerCmd logContainerCmd(String containerId) { + return new LogContainerCmdImpl(getDockerCmdExecFactory() + .createLogContainerCmdExec(), containerId); + } + + + + public CopyFileFromContainerCmd copyFileFromContainerCmd( + String containerId, String resource) { + return new CopyFileFromContainerCmdImpl(getDockerCmdExecFactory() + .createCopyFileFromContainerCmdExec(), containerId, resource); + } + + public ContainerDiffCmd containerDiffCmd(String containerId) { + return new ContainerDiffCmdImpl(getDockerCmdExecFactory() + .createContainerDiffCmdExec(), containerId); + } + + public StopContainerCmd stopContainerCmd(String containerId) { + return new StopContainerCmdImpl(getDockerCmdExecFactory() + .createStopContainerCmdExec(), containerId); + } + + public KillContainerCmd killContainerCmd(String containerId) { + return new KillContainerCmdImpl(getDockerCmdExecFactory() + .createKillContainerCmdExec(), containerId); + } + + public RestartContainerCmd restartContainerCmd(String containerId) { + return new RestartContainerCmdImpl(getDockerCmdExecFactory() + .createRestartContainerCmdExec(), containerId); + } + + public CommitCmd commitCmd(String containerId) { + return new CommitCmdImpl(getDockerCmdExecFactory() + .createCommitCmdExec(), containerId); + } + + public BuildImageCmd buildImageCmd() { + return augmentBuildImageCmd(new BuildImageCmdImpl(getDockerCmdExecFactory() + .createBuildImageCmdExec())); + } + + public BuildImageCmd buildImageCmd(File dockerFileOrFolder) { + return augmentBuildImageCmd(new BuildImageCmdImpl(getDockerCmdExecFactory() + .createBuildImageCmdExec(), dockerFileOrFolder)); + } + + public BuildImageCmd buildImageCmd(InputStream tarInputStream) { + return augmentBuildImageCmd(new BuildImageCmdImpl(getDockerCmdExecFactory() + .createBuildImageCmdExec(), tarInputStream)); + } + + private BuildImageCmd augmentBuildImageCmd(BuildImageCmd buildImageCmd) { + final AuthConfigurations authConfigurations = dockerClientConfig.getAuthConfigurations(); + if (!authConfigurations.getConfigs().isEmpty()) { + buildImageCmd.withBuildAuthConfigs(authConfigurations); + } + + return buildImageCmd; + } + + + public TopContainerCmd topContainerCmd(String containerId) { + return new TopContainerCmdImpl(getDockerCmdExecFactory() + .createTopContainerCmdExec(), containerId); + } + + public TagImageCmd tagImageCmd(String imageId, String repository, String tag) { + return new TagImageCmdImpl(getDockerCmdExecFactory() + .createTagImageCmdExec(), imageId, repository, tag); + } + + public PauseContainerCmd pauseContainerCmd(String containerId) { + return new PauseContainerCmdImpl(getDockerCmdExecFactory() + .createPauseContainerCmdExec(), containerId); + } + + + public UnpauseContainerCmd unpauseContainerCmd(String containerId) { + return new UnpauseContainerCmdImpl(getDockerCmdExecFactory() + .createUnpauseContainerCmdExec(), containerId); + } + + public StatsCmd statsCmd(String containerId) { + return new StatsCmdImpl(getDockerCmdExecFactory() + .createStatsCmdExec(), containerId); + } + + public EventsCmd eventsCmd(EventCallback eventCallback) { + return new EventsCmdImpl(getDockerCmdExecFactory() + .createEventsCmdExec(), eventCallback); + } + + public void close() throws IOException { + getDockerCmdExecFactory().close(); + } + +} diff --git a/src/main/java/com/github/dockerjava/core/FilePathUtil.java b/src/main/java/com/github/dockerjava/core/FilePathUtil.java new file mode 100644 index 00000000..3fc91021 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/FilePathUtil.java @@ -0,0 +1,24 @@ +package com.github.dockerjava.core; + +import java.io.*; +import com.github.dockerjava.api.DockerClientException; + +public class FilePathUtil { + + /** + * Return the relative path. Path elements are separated with / char. + * @param baseDir a parent directory of {@code file} + * @param file the file to get the relative path + * @return the relative path + */ + public static String relativize(File baseDir, File file) { + try { + baseDir = baseDir.getCanonicalFile(); + file = file.getCanonicalFile(); + + return baseDir.toURI().relativize(file.toURI()).getPath(); + } catch (IOException e) { + throw new DockerClientException(e.getMessage(), e); + } + } +} diff --git a/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java b/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java new file mode 100644 index 00000000..a84bce5b --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java @@ -0,0 +1,257 @@ +/** + * Copyright (C) 2014 SignalFuse, Inc. + */ +package com.github.dockerjava.core; + +import java.io.File; +import java.util.List; + +import org.apache.commons.lang.StringUtils; + +/** + * Implementation of golang's file.Match + * + * Match returns true if name matches the shell file name pattern. The pattern syntax is: + * + *
+ *  pattern:
+ *          { term }
+ *   term:
+ *       '*'         matches any sequence of non-Separator characters
+ *      '?'         matches any single non-Separator character
+ *       '[' [ '^' ] { character-range } ']'
+ *                  character class (must be non-empty)
+ *       c           matches character c (c != '*', '?', '\\', '[')
+ *       '\\' c      matches character c
+ *
+ *   character-range:
+ *       c           matches character c (c != '\\', '-', ']')
+ *       '\\' c      matches character c
+ *       lo '-' hi   matches character c for lo <= c <= hi
+ *
+ *  Match requires pattern to match all of name, not just a substring.
+ *  The only possible returned error is ErrBadPattern, when pattern
+ *  is malformed.
+ *
+ * On Windows, escaping is disabled. Instead, '\\' is treated as
+ *  path separator.
+ * 
+ * + * @author tedo + * + */ +public class GoLangFileMatch { + + public static final boolean IS_WINDOWS = File.separatorChar == '\\'; + + public static boolean match(List patterns, File file) { + return match(patterns, file.getPath()); + } + + public static boolean match(String pattern, File file) { + return match(pattern, file.getPath()); + } + + public static boolean match(List patterns, String name) { + for (String pattern : patterns) { + if (match(pattern, name)) { + return true; + } + } + return false; + } + + public static boolean match(String pattern, String name) { + Pattern: while (!pattern.isEmpty()) { + ScanResult scanResult = scanChunk(pattern); + pattern = scanResult.pattern; + if (scanResult.star && StringUtils.isEmpty(scanResult.chunk)) { + // Trailing * matches rest of string unless it has a /. + return name.indexOf(File.separatorChar) < 0; + } + // Look for match at current position. + String matchResult = matchChunk(scanResult.chunk, name); + + // if we're the last chunk, make sure we've exhausted the name + // otherwise we'll give a false result even if we could still match + // using the star + if (matchResult != null && (matchResult.isEmpty() || !pattern.isEmpty())) { + name = matchResult; + continue; + } + if (scanResult.star) { + for (int i = 0; i < name.length() && name.charAt(i) != File.separatorChar; i++) { + matchResult = matchChunk(scanResult.chunk, name.substring(i + 1)); + if (matchResult != null) { + // if we're the last chunk, make sure we exhausted the name + if (pattern.isEmpty() && !matchResult.isEmpty()) { + continue; + } + name = matchResult; + continue Pattern; + } + } + } + return false; + } + return name.isEmpty(); + } + + static ScanResult scanChunk(String pattern) { + boolean star = false; + if (!pattern.isEmpty() && pattern.charAt(0) == '*') { + pattern = pattern.substring(1); + star = true; + } + boolean inRange = false; + int i; + Scan: for (i = 0; i < pattern.length(); i++) { + switch (pattern.charAt(i)) { + case '\\': { + if (!IS_WINDOWS) { + // error check handled in matchChunk: bad pattern. + if (i + 1 < pattern.length()) { + i++; + } + } + break; + } + case '[': + inRange = true; + break; + case ']': + inRange = false; + break; + case '*': + if (!inRange) { + break Scan; + } + } + } + return new ScanResult(star, pattern.substring(0, i), pattern.substring(i)); + } + + static String matchChunk(String chunk, String s) { + int chunkLength = chunk.length(); + int chunkOffset = 0; + int sLength = s.length(); + int sOffset = 0; + char r; + while (chunkOffset < chunkLength) { + if (sOffset == sLength) { + return null; + } + switch (chunk.charAt(chunkOffset)) { + case '[': + r = s.charAt(sOffset); + sOffset++; + chunkOffset++; + // We can't end right after '[', we're expecting at least + // a closing bracket and possibly a caret. + if (chunkOffset == chunkLength) { + throw new GoLangFileMatchException(); + } + // possibly negated + boolean negated = chunk.charAt(chunkOffset) == '^'; + if (negated) { + chunkOffset++; + } + // parse all ranges + boolean match = false; + int nrange = 0; + while (true) { + if (chunkOffset < chunkLength && chunk.charAt(chunkOffset) == ']' && nrange > 0) { + chunkOffset++; + break; + } + GetEscResult result = getEsc(chunk, chunkOffset, chunkLength); + char lo = result.lo; + char hi = lo; + chunkOffset = result.chunkOffset; + if (chunk.charAt(chunkOffset) == '-') { + result = getEsc(chunk, ++chunkOffset, chunkLength); + chunkOffset = result.chunkOffset; + hi = result.lo; + } + if (lo <= r && r <= hi) { + match = true; + } + nrange++; + } + if (match == negated) { + return null; + } + break; + + case '?': + if (s.charAt(sOffset) == File.separatorChar) { + return null; + } + sOffset++; + chunkOffset++; + break; + case '\\': + if (!IS_WINDOWS) { + chunkOffset++; + if (chunkOffset == chunkLength) { + throw new GoLangFileMatchException(); + } + } + // fallthrough + default: + if (chunk.charAt(chunkOffset) != s.charAt(sOffset)) { + return null; + } + sOffset++; + chunkOffset++; + } + } + return s.substring(sOffset); + } + + static GetEscResult getEsc(String chunk, int chunkOffset, int chunkLength) { + if (chunkOffset == chunkLength) { + throw new GoLangFileMatchException(); + } + char r = chunk.charAt(chunkOffset); + if (r == '-' || r == ']') { + throw new GoLangFileMatchException(); + } + if (r == '\\' && !IS_WINDOWS) { + chunkOffset++; + if (chunkOffset == chunkLength) { + throw new GoLangFileMatchException(); + } + + } + r = chunk.charAt(chunkOffset); + chunkOffset++; + if (chunkOffset == chunkLength) { + throw new GoLangFileMatchException(); + } + return new GetEscResult(r, chunkOffset); + } + + private static final class ScanResult { + public boolean star; + public String chunk; + public String pattern; + + public ScanResult(boolean star, String chunk, String pattern) { + this.star = star; + this.chunk = chunk; + this.pattern = pattern; + } + } + + private static final class GetEscResult { + public char lo; + public int chunkOffset; + + public GetEscResult(char lo, int chunkOffset) { + this.lo = lo; + this.chunkOffset = chunkOffset; + } + } + +} diff --git a/src/main/java/com/github/dockerjava/core/GoLangFileMatchException.java b/src/main/java/com/github/dockerjava/core/GoLangFileMatchException.java new file mode 100644 index 00000000..d1f35393 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/GoLangFileMatchException.java @@ -0,0 +1,10 @@ +/** + * Copyright (C) 2014 SignalFuse, Inc. + */ +package com.github.dockerjava.core; + +public class GoLangFileMatchException extends IllegalArgumentException { + + private static final long serialVersionUID = -1204971075600864898L; + +} diff --git a/src/main/java/com/github/dockerjava/core/GoLangMatchFileFilter.java b/src/main/java/com/github/dockerjava/core/GoLangMatchFileFilter.java new file mode 100644 index 00000000..33e0f2a4 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/GoLangMatchFileFilter.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2014 SignalFuse, Inc. + */ +package com.github.dockerjava.core; + +import java.io.File; +import java.util.List; + +import org.apache.commons.io.filefilter.AbstractFileFilter; + +public class GoLangMatchFileFilter extends AbstractFileFilter { + + private final File base; + + private final List patterns; + + + public GoLangMatchFileFilter(File base, List patterns) { + super(); + this.base = base; + this.patterns = patterns; + } + + @Override + public boolean accept(File file) { + String relativePath = FilePathUtil.relativize(base, file); + + boolean match = GoLangFileMatch.match(patterns, relativePath); + return !match; + } + + +} diff --git a/src/main/java/com/github/dockerjava/core/InvalidRepositoryNameException.java b/src/main/java/com/github/dockerjava/core/InvalidRepositoryNameException.java new file mode 100644 index 00000000..8885fc75 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/InvalidRepositoryNameException.java @@ -0,0 +1,15 @@ +package com.github.dockerjava.core; + +public class InvalidRepositoryNameException extends IllegalArgumentException { + + private static final long serialVersionUID = -6908709623436840513L; + + public InvalidRepositoryNameException() { + super(); + } + + public InvalidRepositoryNameException(String s) { + super(s); + } + +} diff --git a/src/main/java/com/github/dockerjava/core/KeystoreSSLConfig.java b/src/main/java/com/github/dockerjava/core/KeystoreSSLConfig.java new file mode 100644 index 00000000..ec5a2ba8 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/KeystoreSSLConfig.java @@ -0,0 +1,134 @@ +package com.github.dockerjava.core; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.Serializable; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +/** + * An SSL Config that is based on an pre-existing or pre-loaded KeyStore. + */ +public class KeystoreSSLConfig implements SSLConfig, Serializable { + + private final KeyStore keystore; + private final String keystorePassword; + + /** + * @param keystore a KeyStore + * @param keystorePassword key password + */ + public KeystoreSSLConfig(KeyStore keystore, String keystorePassword) { + this.keystorePassword = keystorePassword; + checkNotNull(keystore); + this.keystore = keystore; + } + + /** + * + * @param pfxFile a PKCS12 file + * @param password Password for the keystore + * @throws KeyStoreException + * @throws IOException + * @throws CertificateException + * @throws NoSuchAlgorithmException + */ + public KeystoreSSLConfig(File pfxFile, String password) + throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException { + checkNotNull(pfxFile); + checkNotNull(password); + keystore = KeyStore.getInstance("pkcs12"); + keystore.load(new FileInputStream(pfxFile), password.toCharArray()); + keystorePassword = password; + } + + + /** + * Get the SSL Context out of the keystore. + * @return java SSLContext + * @throws KeyManagementException + * @throws UnrecoverableKeyException + * @throws NoSuchAlgorithmException + * @throws KeyStoreException + */ + @Override + public SSLContext getSSLContext() + throws KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, + KeyStoreException { + + final SSLContext context = SSLContext.getInstance("TLS"); + + String httpProtocols = System.getProperty("https.protocols"); + System.setProperty("https.protocols", "TLSv1"); + + if (httpProtocols != null) + System.setProperty("https.protocols", httpProtocols); + + final KeyManagerFactory + keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keystore, keystorePassword.toCharArray()); + context.init(keyManagerFactory.getKeyManagers(), new TrustManager[]{ + new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + + @Override + public void checkClientTrusted(final X509Certificate[] arg0, final String arg1) { + + } + + @Override + public void checkServerTrusted(final X509Certificate[] arg0, final String arg1) { + + } + } + }, new SecureRandom()); + + return context; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + KeystoreSSLConfig that = (KeystoreSSLConfig) o; + + return keystore.equals(that.keystore); + + } + + @Override + public int hashCode() { + return keystore.hashCode(); + } + + @Override + public String toString() { + return new StringBuilder() + .append(this.getClass().getSimpleName()).append("{") + .append("keystore=").append(keystore) + .append("}") + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/LocalDirectorySSLConfig.java b/src/main/java/com/github/dockerjava/core/LocalDirectorySSLConfig.java new file mode 100644 index 00000000..bafe6221 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/LocalDirectorySSLConfig.java @@ -0,0 +1,99 @@ +package com.github.dockerjava.core; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.security.Security; + +import javax.net.ssl.SSLContext; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.glassfish.jersey.SslConfigurator; + +import com.github.dockerjava.api.DockerClientException; + + +/** + * SSL Config from local files. + */ +public class LocalDirectorySSLConfig implements SSLConfig, Serializable { + + private final String dockerCertPath; + + public LocalDirectorySSLConfig(String dockerCertPath) { + checkNotNull(dockerCertPath); + this.dockerCertPath = dockerCertPath; + } + + public String getDockerCertPath() { + return dockerCertPath; + } + + @Override + public SSLContext getSSLContext() { + + boolean certificatesExist = CertificateUtils.verifyCertificatesExist(dockerCertPath); + + if (certificatesExist) { + + try { + + Security.addProvider(new BouncyCastleProvider()); + + // properties acrobatics not needed for java > 1.6 + String httpProtocols = System.getProperty("https.protocols"); + System.setProperty("https.protocols", "TLSv1"); + SslConfigurator sslConfig = SslConfigurator.newInstance(true); + if (httpProtocols != null) { + System.setProperty("https.protocols", httpProtocols); + } + + sslConfig.keyStore(CertificateUtils.createKeyStore(dockerCertPath)); + sslConfig.keyStorePassword("docker"); + sslConfig.trustStore(CertificateUtils.createTrustStore(dockerCertPath)); + + return sslConfig.createSSLContext(); + + + } catch (Exception e) { + throw new DockerClientException(e.getMessage(), e); + } + + } + + return null; + + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + LocalDirectorySSLConfig that = (LocalDirectorySSLConfig) o; + + if (!dockerCertPath.equals(that.dockerCertPath)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return dockerCertPath.hashCode(); + } + + @Override + public String toString() { + return new StringBuilder() + .append(this.getClass().getSimpleName()).append("{") + .append("dockerCertPath=").append(dockerCertPath) + .append("}") + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/NameParser.java b/src/main/java/com/github/dockerjava/core/NameParser.java new file mode 100644 index 00000000..6b165408 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/NameParser.java @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2014 SignalFuse, Inc. + */ +package com.github.dockerjava.core; + +import java.util.regex.Pattern; + +import com.github.dockerjava.api.model.AuthConfig; + +public class NameParser { + + private static final Pattern VALID_HEX_PATTERN = Pattern.compile("^([a-f0-9]{64})$"); + private static final Pattern VALID_NAMESPACE_PATTERN = Pattern.compile("^([a-z0-9_]{4,30})$"); + private static final Pattern VALID_REPO_PATTERN = Pattern.compile("^([a-z0-9-_.]+)$"); + + public static ReposTag parseRepositoryTag(String name) { + int n = name.lastIndexOf(':'); + if (n < 0) { + return new ReposTag(name, ""); + } + String tag = name.substring(n + 1); + if (!tag.contains("/")) { + return new ReposTag(name.substring(0, n), tag); + } + return new ReposTag(name, ""); + } + + public static class ReposTag { + public final String repos; + public final String tag; + + public ReposTag(String repos, String tag) { + this.repos = repos; + this.tag = tag; + } + } + + public static void validateRepositoryName(String repositoryName) { + String name; + String namespace; + String[] nameParts = repositoryName.split("/", 2); + if (nameParts.length < 2) { + namespace = "library"; + name = nameParts[0]; + if (VALID_HEX_PATTERN.matcher(name).matches()) { + throw new InvalidRepositoryNameException(String.format( + "Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", + name)); + } + } else { + namespace = nameParts[0]; + name = nameParts[1]; + } + if (!VALID_NAMESPACE_PATTERN.matcher(namespace).matches()) { + throw new InvalidRepositoryNameException( + String.format( + "Invalid namespace name (%s), only [a-z0-9_] are allowed, size between 4 and 30", + namespace)); + } + if (!VALID_REPO_PATTERN.matcher(name).matches()) { + throw new InvalidRepositoryNameException(String.format( + "Invalid repository name (%s), only [a-z0-9-_.] are allowed", name)); + } + } + + public static HostnameReposName resolveRepositoryName(String reposName) { + if (reposName.contains("://")) { + // It cannot contain a scheme! + throw new InvalidRepositoryNameException(); + } + + String[] nameParts = reposName.split("/", 2); + if (nameParts.length == 1 + || (!nameParts[0].contains(".") && !nameParts[0].contains(":") && !nameParts[0] + .equals("localhost"))) { + return new HostnameReposName(AuthConfig.DEFAULT_SERVER_ADDRESS, reposName); + } + + String hostname = nameParts[0]; + reposName = nameParts[1]; + if (hostname.contains("index.docker.io")) { + throw new InvalidRepositoryNameException(String.format( + "Invalid repository name, try \"%s\" instead", reposName)); + } + + validateRepositoryName(reposName); + return new HostnameReposName(hostname, reposName); + } + + public static class HostnameReposName { + public final String hostname; + public final String reposName; + + public HostnameReposName(String hostname, String reposName) { + this.hostname = hostname; + this.reposName = reposName; + } + + } +} diff --git a/src/main/java/com/github/dockerjava/core/SSLConfig.java b/src/main/java/com/github/dockerjava/core/SSLConfig.java new file mode 100644 index 00000000..ab639489 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/SSLConfig.java @@ -0,0 +1,19 @@ +package com.github.dockerjava.core; + +import java.security.*; + +import javax.net.ssl.SSLContext; + +/** + * Get an SSL Config. Allows for various different implementations. + */ +public interface SSLConfig { + + /** + * Get the SSL Context, from wherever it comes (file, keystore). + * @return an SSL context. + */ + SSLContext getSSLContext() + throws KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, + KeyStoreException; +} diff --git a/src/main/java/com/github/dockerjava/core/StatsCmdImpl.java b/src/main/java/com/github/dockerjava/core/StatsCmdImpl.java new file mode 100644 index 00000000..215825e3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/StatsCmdImpl.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.core; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.command.StatsCmd; +import com.github.dockerjava.api.model.Stats; +import com.github.dockerjava.core.command.AbstrDockerCmd; + +/** + * @author Heng WU(wuheng09@otcaix.iscas.ac.cn) + * + */ +public class StatsCmdImpl extends AbstrDockerCmd implements + StatsCmd { + + private String containerId; + + public StatsCmdImpl(StatsCmd.Exec exec, String containerId) { + super(exec); + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + } + + public String getContainerId() { + return containerId; + } + + @Override + public String toString() { + return "stats"; + } + +} diff --git a/src/main/java/com/github/dockerjava/core/command/AbstrAuthCfgDockerCmd.java b/src/main/java/com/github/dockerjava/core/command/AbstrAuthCfgDockerCmd.java new file mode 100644 index 00000000..31259f1f --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/AbstrAuthCfgDockerCmd.java @@ -0,0 +1,51 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; + +import org.apache.commons.codec.binary.Base64; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.command.DockerCmd; +import com.github.dockerjava.api.command.DockerCmdExec; +import com.github.dockerjava.api.model.AuthConfig; + +public abstract class AbstrAuthCfgDockerCmd, RES_T> extends + AbstrDockerCmd { + + public AbstrAuthCfgDockerCmd(DockerCmdExec execution, AuthConfig authConfig) { + super(execution); + withOptionalAuthConfig(authConfig); + } + + public AbstrAuthCfgDockerCmd(DockerCmdExec execution) { + super(execution); + } + + private AuthConfig authConfig; + + public AuthConfig getAuthConfig() { + return authConfig; + } + + public T withAuthConfig(AuthConfig authConfig) { + checkNotNull(authConfig, "authConfig was not specified"); + return withOptionalAuthConfig(authConfig); + } + + @SuppressWarnings("unchecked") + private T withOptionalAuthConfig(AuthConfig authConfig) { + this.authConfig = authConfig; + return (T)this; + } + + protected String registryAuth() { + try { + return Base64.encodeBase64String(new ObjectMapper().writeValueAsString(authConfig).getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/core/command/AbstrDockerCmd.java b/src/main/java/com/github/dockerjava/core/command/AbstrDockerCmd.java new file mode 100644 index 00000000..751b02cb --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/AbstrDockerCmd.java @@ -0,0 +1,34 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.DockerCmd; +import com.github.dockerjava.api.command.DockerCmdExec; + +public abstract class AbstrDockerCmd, RES_T> implements DockerCmd { + + private final static Logger LOGGER = LoggerFactory.getLogger(AbstrDockerCmd.class); + + protected DockerCmdExec execution; + + public AbstrDockerCmd(DockerCmdExec execution) { + checkNotNull(execution, "execution was not specified"); + this.execution = execution; + } + + @Override + @SuppressWarnings("unchecked") + public RES_T exec() throws DockerException { + LOGGER.debug("Cmd: {}", this); + return execution.exec((CMD_T)this); + } + + @Override + public void close() throws IOException {} +} diff --git a/src/main/java/com/github/dockerjava/core/command/AttachContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/AttachContainerCmdImpl.java new file mode 100644 index 00000000..fc38ae7d --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/AttachContainerCmdImpl.java @@ -0,0 +1,131 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.AttachContainerCmd; + +/** + * Attach to container + * + * @param logs + * - true or false, includes logs. Defaults to false. + * + * @param followStream + * - true or false, return stream. Defaults to false. + * @param stdout + * - true or false, includes stdout log. Defaults to false. + * @param stderr + * - true or false, includes stderr log. Defaults to false. + * @param timestamps + * - true or false, if true, print timestamps for every log line. + * Defaults to false. + */ +public class AttachContainerCmdImpl extends AbstrDockerCmd implements AttachContainerCmd { + + private String containerId; + + private boolean logs, followStream, timestamps, stdout, stderr; + + public AttachContainerCmdImpl(AttachContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public boolean hasLogsEnabled() { + return logs; + } + + @Override + public boolean hasFollowStreamEnabled() { + return followStream; + } + + @Override + public boolean hasTimestampsEnabled() { + return timestamps; + } + + @Override + public boolean hasStdoutEnabled() { + return stdout; + } + + @Override + public boolean hasStderrEnabled() { + return stderr; + } + + @Override + public AttachContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public AttachContainerCmd withFollowStream() { + return withFollowStream(true); + } + + @Override + public AttachContainerCmd withFollowStream(boolean followStream) { + this.followStream = followStream; + return this; + } + + @Override + public AttachContainerCmd withTimestamps(boolean timestamps) { + this.timestamps = timestamps; + return this; + } + + @Override + public AttachContainerCmd withStdOut() { + return withStdOut(true); + } + + @Override + public AttachContainerCmd withStdOut(boolean stdout) { + this.stdout = stdout; + return this; + } + + @Override + public AttachContainerCmd withStdErr() { + return withStdErr(true); + } + + @Override + public AttachContainerCmd withStdErr(boolean stderr) { + this.stderr = stderr; + return this; + } + + @Override + public AttachContainerCmd withLogs(boolean logs) { + this.logs = logs; + return this; + } + + @Override + public AttachContainerCmd withLogs() { + return withLogs(true); + } + + /** + * @throws NotFoundException No such container + */ + @Override + public InputStream exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/AuthCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/AuthCmdImpl.java new file mode 100644 index 00000000..0da0da3c --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/AuthCmdImpl.java @@ -0,0 +1,29 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.UnauthorizedException; +import com.github.dockerjava.api.command.AuthCmd; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.AuthResponse; + +/** + * + * Authenticate with the server, useful for checking authentication. + * + */ +public class AuthCmdImpl extends AbstrAuthCfgDockerCmd implements AuthCmd { + + public AuthCmdImpl(AuthCmd.Exec exec, AuthConfig authConfig) { + super(exec); + withAuthConfig(authConfig); + } + + @Override + public AuthResponse exec() throws UnauthorizedException { + return super.exec(); + } + + @Override + public String toString() { + return "authenticate using " + this.getAuthConfig(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java new file mode 100644 index 00000000..ef024285 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java @@ -0,0 +1,214 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import com.github.dockerjava.api.command.BuildImageCmd; +import com.github.dockerjava.api.model.AuthConfigurations; +import com.github.dockerjava.core.FilePathUtil; +import com.github.dockerjava.core.dockerfile.Dockerfile; + +/** + * + * Build an image from Dockerfile. + * + */ +public class BuildImageCmdImpl extends AbstrDockerCmd implements BuildImageCmd { + + + private InputStream tarInputStream = null; + private String tag; + private boolean noCache; + private boolean remove = true; + private boolean quiet; + private boolean pull; + + private AuthConfigurations buildAuthConfigs; + private File dockerFile; + private File baseDirectory; + + public BuildImageCmdImpl(BuildImageCmd.Exec exec) { + super(exec); + } + + public BuildImageCmdImpl(BuildImageCmd.Exec exec, File dockerFileOrFolder) { + super(exec); + checkNotNull(dockerFileOrFolder, "dockerFolder is null"); + + if( dockerFileOrFolder.isDirectory() ) { + withBaseDirectory(dockerFileOrFolder); + withDockerfile(new File(dockerFileOrFolder, "Dockerfile")); + } + else { + withDockerfile(dockerFileOrFolder); + } + } + + public BuildImageCmdImpl(BuildImageCmd.Exec exec, InputStream tarInputStream) { + super(exec); + checkNotNull(tarInputStream, "tarInputStream is null"); + withTarInputStream(tarInputStream); + } + + @Override + public InputStream getTarInputStream() { + return tarInputStream; + } + + @Override + public BuildImageCmdImpl withDockerfile(File dockerfile) { + checkNotNull(dockerfile); + if( !dockerfile.exists() ) + throw new IllegalArgumentException("Dockerfile does not exist"); + if( !dockerfile.isFile() ) + throw new IllegalArgumentException("Not a directory"); + + if( baseDirectory == null ) + withBaseDirectory(dockerfile.getParentFile()); + + + this.dockerFile = dockerfile; + + try { + withTarInputStream( + new Dockerfile(dockerfile) + .parse() + .buildDockerFolderTar(baseDirectory) ); + } catch (IOException e) { + // we just created the file this should never happen. + throw new RuntimeException(e); + } + return this; + } + + @Override + public BuildImageCmdImpl withTarInputStream(InputStream tarInputStream) { + checkNotNull(tarInputStream, "tarInputStream is null"); + this.tarInputStream = tarInputStream; + return this; + } + + @Override + public BuildImageCmdImpl withTag(String tag) { + checkNotNull(tag, "Tag is null"); + this.tag = tag; + return this; + } + + @Override + public String getTag() { + return tag; + } + + @Override + public boolean hasNoCacheEnabled() { + return noCache; + } + + @Override + public boolean hasRemoveEnabled() { + return remove; + } + + @Override + public boolean isQuiet() { + return quiet; + } + + @Override + public boolean hasPullEnabled() { + return pull; + } + + @Override + public String getPathToDockerfile() { + if (baseDirectory != null && dockerFile != null) { + return FilePathUtil.relativize(baseDirectory, dockerFile); + } else { + return null; + } + } + + @Override + public AuthConfigurations getBuildAuthConfigs() { + return buildAuthConfigs; + } + + @Override + public BuildImageCmd withBaseDirectory(File baseDirectory) { + this.baseDirectory = baseDirectory; + return this; + } + + @Override + public BuildImageCmdImpl withNoCache() { + return withNoCache(true); + } + + @Override + public BuildImageCmdImpl withNoCache(boolean noCache) { + this.noCache = noCache; + return this; + } + + @Override + public BuildImageCmdImpl withRemove() { + return withRemove(true); + } + + @Override + public BuildImageCmdImpl withRemove(boolean rm) { + this.remove = rm; + return this; + } + + @Override + public BuildImageCmdImpl withQuiet() { + return withQuiet(true); + } + + @Override + public BuildImageCmdImpl withQuiet(boolean quiet) { + this.quiet = quiet; + return this; + } + + @Override + public BuildImageCmdImpl withPull() { + return withPull(true); + } + + @Override + public BuildImageCmdImpl withPull(boolean pull) { + this.pull = pull; + return this; + } + + @Override + public BuildImageCmd withBuildAuthConfigs(AuthConfigurations authConfigs) { + checkNotNull(authConfigs, "authConfig is null"); + this.buildAuthConfigs = authConfigs; + return this; + } + + @Override + public void close() throws IOException { + super.close(); + + tarInputStream.close(); + } + + @Override + public String toString() { + return new StringBuilder("build ") + .append(tag != null ? "-t " + tag + " " : "") + .append(noCache ? "--nocache=true " : "") + .append(quiet ? "--quiet=true " : "") + .append(!remove ? "--rm=false " : "").toString(); + } + + +} diff --git a/src/main/java/com/github/dockerjava/core/command/CommitCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/CommitCmdImpl.java new file mode 100644 index 00000000..55fe5e2e --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/CommitCmdImpl.java @@ -0,0 +1,371 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CommitCmd; +import com.github.dockerjava.api.model.ExposedPorts; +import com.github.dockerjava.api.model.Volumes; + + +/** + * + * Create a new image from a container's changes. Returns the new image ID. + * + */ +public class CommitCmdImpl extends AbstrDockerCmd implements CommitCmd { + + private String containerId, repository, tag, message, author; + + private boolean pause = true; + + @JsonProperty("AttachStdin") + private boolean attachStdin; + + @JsonProperty("AttachStdout") + private boolean attachStdout; + + @JsonProperty("AttachStderr") + private boolean attachStderr; + + @JsonProperty("Cmd") + private String[] cmd; + + @JsonProperty("DisableNetwork") + private boolean disableNetwork; + + @JsonProperty("Env") + private String[] env; + + @JsonProperty("ExposedPorts") + private ExposedPorts exposedPorts; + + @JsonProperty("Hostname") + private String hostname; + + @JsonProperty("Memory") + private Integer memory; + + @JsonProperty("MemorySwap") + private Integer memorySwap; + + @JsonProperty("OpenStdin") + private boolean openStdin; + + @JsonProperty("PortSpecs") + private String[] portSpecs; + + @JsonProperty("StdinOnce") + private boolean stdinOnce; + + @JsonProperty("Tty") + private boolean tty; + + @JsonProperty("User") + private String user; + + @JsonProperty("Volumes") + private Volumes volumes; + + @JsonProperty("WorkingDir") + private String workingDir; + + + public CommitCmdImpl(CommitCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public CommitCmdImpl withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + + @Override + public String getRepository() { + return repository; + } + + @Override + public String getTag() { + return tag; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public String getAuthor() { + return author; + } + + @Override + public boolean hasPauseEnabled() { + return pause; + } + + @Override + public CommitCmdImpl withAttachStderr(boolean attachStderr) { + this.attachStderr = attachStderr; + return this; + } + + @Override + public CommitCmdImpl withAttachStderr() { + return withAttachStderr(true); + } + + @Override + public CommitCmdImpl withAttachStdin(boolean attachStdin) { + this.attachStdin = attachStdin; + return this; + } + + @Override + public CommitCmdImpl withAttachStdin() { + return withAttachStdin(true); + } + + @Override + public CommitCmdImpl withAttachStdout(boolean attachStdout) { + this.attachStdout = attachStdout; + return this; + } + + @Override + public CommitCmdImpl withAttachStdout() { + return withAttachStdout(true); + } + + @Override + public CommitCmdImpl withCmd(String... cmd) { + checkNotNull(cmd, "cmd was not specified"); + this.cmd = cmd; + return this; + } + + @Override + public CommitCmdImpl withDisableNetwork(boolean disableNetwork) { + this.disableNetwork = disableNetwork; + return this; + } + + @Override + public CommitCmdImpl withAuthor(String author) { + checkNotNull(author, "author was not specified"); + this.author = author; + return this; + } + + @Override + public CommitCmdImpl withMessage(String message) { + checkNotNull(message, "message was not specified"); + this.message = message; + return this; + } + + @Override + public CommitCmdImpl withTag(String tag) { + checkNotNull(tag, "tag was not specified"); + this.tag = tag; + return this; + } + + @Override + public CommitCmdImpl withRepository(String repository) { + checkNotNull(repository, "repository was not specified"); + this.repository = repository; + return this; + } + + @Override + public CommitCmdImpl withPause(boolean pause) { + this.pause = pause; + return this; + } + + @Override + public String[] getEnv() { + return env; + } + + @Override + public CommitCmdImpl withEnv(String... env) { + checkNotNull(env, "env was not specified"); + this.env = env; + return this; + } + + @Override + public ExposedPorts getExposedPorts() { + return exposedPorts; + } + + @Override + public CommitCmdImpl withExposedPorts(ExposedPorts exposedPorts) { + checkNotNull(exposedPorts, "exposedPorts was not specified"); + this.exposedPorts = exposedPorts; + return this; + } + + @Override + public String getHostname() { + return hostname; + } + + @Override + public CommitCmdImpl withHostname(String hostname) { + checkNotNull(hostname, "hostname was not specified"); + this.hostname = hostname; + return this; + } + + @Override + public Integer getMemory() { + return memory; + } + + @Override + public CommitCmdImpl withMemory(Integer memory) { + checkNotNull(memory, "memory was not specified"); + this.memory = memory; + return this; + } + + @Override + public Integer getMemorySwap() { + return memorySwap; + } + + @Override + public CommitCmdImpl withMemorySwap(Integer memorySwap) { + checkNotNull(memorySwap, "memorySwap was not specified"); + this.memorySwap = memorySwap; + return this; + } + + @Override + public boolean isOpenStdin() { + return openStdin; + } + + @Override + public CommitCmdImpl withOpenStdin(boolean openStdin) { + checkNotNull(openStdin, "openStdin was not specified"); + this.openStdin = openStdin; + return this; + } + + @Override + public String[] getPortSpecs() { + return portSpecs; + } + + @Override + public CommitCmdImpl withPortSpecs(String... portSpecs) { + checkNotNull(portSpecs, "portSpecs was not specified"); + this.portSpecs = portSpecs; + return this; + } + + @Override + public boolean isStdinOnce() { + return stdinOnce; + } + + @Override + public CommitCmdImpl withStdinOnce(boolean stdinOnce) { + this.stdinOnce = stdinOnce; + return this; + } + + @Override + public CommitCmdImpl withStdinOnce() { + return withStdinOnce(true); + } + + @Override + public boolean isTty() { + return tty; + } + + @Override + public CommitCmdImpl withTty(boolean tty) { + this.tty = tty; + return this; + } + + @Override + public CommitCmdImpl withTty() { + return withTty(true); + } + + @Override + public String getUser() { + return user; + } + + @Override + public CommitCmdImpl withUser(String user) { + checkNotNull(user, "user was not specified"); + this.user = user; + return this; + } + + @Override + public Volumes getVolumes() { + return volumes; + } + + @Override + public CommitCmdImpl withVolumes(Volumes volumes) { + checkNotNull(volumes, "volumes was not specified"); + this.volumes = volumes; + return this; + } + + @Override + public String getWorkingDir() { + return workingDir; + } + + @Override + public CommitCmdImpl withWorkingDir(String workingDir) { + checkNotNull(workingDir, "workingDir was not specified"); + this.workingDir = workingDir; + return this; + } + + + @Override + public String toString() { + return new ToStringBuilder(this).append("commit ") + .append(author != null ? "--author " + author + " " : "") + .append(message != null ? "--message " + message + " " : "") + .append(containerId) + .append(repository != null ? " " + repository + ":" : " ") + .append(tag != null ? tag : "") + .toString(); + } + + /** + * @throws NotFoundException No such container + */ + @Override + public String exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/ContainerDiffCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/ContainerDiffCmdImpl.java new file mode 100644 index 00000000..36286423 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/ContainerDiffCmdImpl.java @@ -0,0 +1,54 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.InternalServerErrorException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.model.ChangeLog; + +/** + * Inspect changes on a container's filesystem + * + * @param containerId - Id of the container + * + */ +public class ContainerDiffCmdImpl extends AbstrDockerCmd> implements ContainerDiffCmd { + + private String containerId; + + public ContainerDiffCmdImpl(ContainerDiffCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public ContainerDiffCmdImpl withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public String toString() { + return new StringBuilder("diff ").append(containerId).toString(); + } + + /** + * @throws NotFoundException No such container + * @throws InternalServerErrorException server error + * @throws DockerException unexpected http status code + */ + @Override + public List exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/CopyFileFromContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/CopyFileFromContainerCmdImpl.java new file mode 100644 index 00000000..f4f2ae54 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/CopyFileFromContainerCmdImpl.java @@ -0,0 +1,87 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; + +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CopyFileFromContainerCmd; + + +/** + * + * Copy files or folders from a container. + * + */ +public class CopyFileFromContainerCmdImpl extends AbstrDockerCmd implements CopyFileFromContainerCmd { + + private String containerId; + + @JsonProperty("HostPath") + private String hostPath = "."; + + @JsonProperty("Resource") + private String resource; + + public CopyFileFromContainerCmdImpl(CopyFileFromContainerCmd.Exec exec, String containerId, String resource) { + super(exec); + withContainerId(containerId); + withResource(resource); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public String getResource() { + return resource; + } + + @Override + public CopyFileFromContainerCmdImpl withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public CopyFileFromContainerCmdImpl withResource(String resource) { + checkNotNull(resource, "resource was not specified"); + this.resource = resource; + return this; + } + + @Override + public String getHostPath() { + return hostPath; + } + + @Override + public CopyFileFromContainerCmdImpl withHostPath(String hostPath) { + checkNotNull(hostPath, "hostPath was not specified"); + this.hostPath = hostPath; + return this; + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("cp ") + .append(containerId) + .append(":") + .append(resource) + .toString(); + } + + /** + * @throws NotFoundException No such container + */ + @Override + public InputStream exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java new file mode 100644 index 00000000..18b089d4 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java @@ -0,0 +1,567 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.dockerjava.api.ConflictException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Capability; +import com.github.dockerjava.api.model.Device; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.ExposedPorts; +import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.Link; +import com.github.dockerjava.api.model.LxcConf; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.api.model.RestartPolicy; +import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumesFrom; +import com.github.dockerjava.api.model.Volumes; + +/** + * + * Creates a new container. + * + */ +public class CreateContainerCmdImpl extends + AbstrDockerCmd implements + CreateContainerCmd { + + private String name; + + @JsonProperty("Hostname") + private String hostName = ""; + @JsonProperty("User") + private String user = ""; + @JsonProperty("Memory") + private long memoryLimit = 0; + @JsonProperty("MemorySwap") + private long memorySwap = 0; + @JsonProperty("CpuShares") + private int cpuShares = 0; + @JsonProperty("Cpuset") + private String cpuset; + @JsonProperty("AttachStdin") + private boolean attachStdin = false; + @JsonProperty("AttachStdout") + private boolean attachStdout = false; + @JsonProperty("AttachStderr") + private boolean attachStderr = false; + @JsonProperty("PortSpecs") + private String[] portSpecs; + @JsonProperty("Tty") + private boolean tty = false; + @JsonProperty("OpenStdin") + private boolean stdinOpen = false; + @JsonProperty("StdinOnce") + private boolean stdInOnce = false; + @JsonProperty("Env") + private String[] env; + @JsonProperty("Cmd") + private String[] cmd; + @JsonProperty("Entrypoint") + private String[] entrypoint; + @JsonProperty("Image") + private String image; + @JsonProperty("Volumes") + private Volumes volumes = new Volumes(); + @JsonProperty("WorkingDir") + private String workingDir = ""; + @JsonProperty("MacAddress") + private String macAddress; + @JsonProperty("NetworkDisabled") + private boolean networkDisabled = false; + @JsonProperty("ExposedPorts") + private ExposedPorts exposedPorts = new ExposedPorts(); + @JsonProperty("HostConfig") + private HostConfig hostConfig = new HostConfig(); + + public CreateContainerCmdImpl(CreateContainerCmd.Exec exec, String image) { + super(exec); + checkNotNull(image, "image was not specified"); + withImage(image); + } + + /** + * @throws NotFoundException + * No such container + * @throws ConflictException + * Named container already exists + */ + @Override + public CreateContainerResponse exec() throws NotFoundException, + ConflictException { + return super.exec(); + } + + @Override + @JsonIgnore + public Bind[] getBinds() { + return hostConfig.getBinds(); + } + + @Override + public Capability[] getCapAdd() { + return hostConfig.getCapAdd(); + } + + @Override + public Capability[] getCapDrop() { + return hostConfig.getCapDrop(); + } + + @Override + public String[] getCmd() { + return cmd; + } + + @Override + public String getCpuset() { + return cpuset; + } + + @Override + public int getCpuShares() { + return cpuShares; + } + + @Override + @JsonIgnore + public Device[] getDevices() { + return hostConfig.getDevices(); + } + + @Override + @JsonIgnore + public String[] getDns() { + return hostConfig.getDns(); + } + + @Override + @JsonIgnore + public String[] getDnsSearch() { + return hostConfig.getDnsSearch(); + } + + @Override + public String[] getEntrypoint() { + return entrypoint; + } + + @Override + public String[] getEnv() { + return env; + } + + @Override + @JsonIgnore + public ExposedPort[] getExposedPorts() { + return exposedPorts.getExposedPorts(); + } + + @Override + @JsonIgnore + public String[] getExtraHosts() { + return hostConfig.getExtraHosts(); + } + + @Override + public HostConfig getHostConfig() { + return hostConfig; + } + + @Override + public String getHostName() { + return hostName; + } + + @Override + public String getImage() { + return image; + } + + @Override + @JsonIgnore + public Link[] getLinks() { + return hostConfig.getLinks(); + } + + @Override + @JsonIgnore + public LxcConf[] getLxcConf() { + return hostConfig.getLxcConf(); + } + + public String getMacAddress() { + return macAddress; + } + + @Override + public long getMemoryLimit() { + return memoryLimit; + } + + @Override + public long getMemorySwap() { + return memorySwap; + } + + @Override + public String getName() { + return name; + } + + @Override + @JsonIgnore + public String getNetworkMode() { + return hostConfig.getNetworkMode(); + } + + @Override + @JsonIgnore + public Ports getPortBindings() { + return hostConfig.getPortBindings(); + } + + @Override + public String[] getPortSpecs() { + return portSpecs; + } + + @Override + @JsonIgnore + public RestartPolicy getRestartPolicy() { + return hostConfig.getRestartPolicy(); + } + + @Override + public String getUser() { + return user; + } + + @Override + @JsonIgnore + public Volume[] getVolumes() { + return volumes.getVolumes(); + } + + @Override + @JsonIgnore + public VolumesFrom[] getVolumesFrom() { + return hostConfig.getVolumesFrom(); + } + + @Override + public String getWorkingDir() { + return workingDir; + } + + @Override + public boolean isAttachStderr() { + return attachStderr; + } + + @Override + public boolean isAttachStdin() { + return attachStdin; + } + + @Override + public boolean isAttachStdout() { + return attachStdout; + } + + @Override + public boolean isNetworkDisabled() { + return networkDisabled; + } + + @Override + @JsonIgnore + public Boolean isPrivileged() { + return hostConfig.isPrivileged(); + } + + @Override + @JsonIgnore + public Boolean isPublishAllPorts() { + return hostConfig.isPublishAllPorts(); + } + + @Override + public boolean isStdInOnce() { + return stdInOnce; + } + + @Override + public boolean isStdinOpen() { + return stdinOpen; + } + + @Override + public boolean isTty() { + return tty; + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("create container ") + .append(name != null ? "name=" + name + " " : "").append(this) + .toString(); + } + + @Override + public CreateContainerCmdImpl withAttachStderr(boolean attachStderr) { + this.attachStderr = attachStderr; + return this; + } + + @Override + public CreateContainerCmdImpl withAttachStdin(boolean attachStdin) { + this.attachStdin = attachStdin; + return this; + } + + @Override + public CreateContainerCmdImpl withAttachStdout(boolean attachStdout) { + this.attachStdout = attachStdout; + return this; + } + + @Override + public CreateContainerCmd withBinds(Bind... binds) { + hostConfig.setBinds(binds); + return this; + } + + @Override + public CreateContainerCmd withCapAdd(Capability... capAdd) { + hostConfig.setCapAdd(capAdd); + return this; + } + + @Override + public CreateContainerCmd withCapDrop(Capability... capDrop) { + hostConfig.setCapDrop(capDrop); + return this; + } + + @Override + public CreateContainerCmdImpl withCmd(String... cmd) { + this.cmd = cmd; + return this; + } + + @Override + public CreateContainerCmdImpl withCpuset(String cpuset) { + this.cpuset = cpuset; + return this; + } + + @Override + public CreateContainerCmdImpl withCpuShares(int cpuShares) { + this.cpuShares = cpuShares; + return this; + } + + @Override + public CreateContainerCmd withDevices(Device... devices) { + this.hostConfig.setDevices(devices); + return this; + } + + @Override + public CreateContainerCmdImpl withNetworkDisabled(boolean disableNetwork) { + this.networkDisabled = disableNetwork; + return this; + } + + @Override + public CreateContainerCmdImpl withDns(String... dns) { + this.hostConfig.setDns(dns); + return this; + } + + @Override + public CreateContainerCmd withDnsSearch(String... dnsSearch) { + this.hostConfig.setDnsSearch(dnsSearch); + return this; + } + + @Override + public CreateContainerCmdImpl withEntrypoint(String... entrypoint) { + this.entrypoint = entrypoint; + return this; + } + + @Override + public CreateContainerCmdImpl withEnv(String... env) { + this.env = env; + return this; + } + + @Override + public CreateContainerCmdImpl withExposedPorts(ExposedPort... exposedPorts) { + this.exposedPorts = new ExposedPorts(exposedPorts); + return this; + } + + @Override + public CreateContainerCmd withExtraHosts(String... extraHosts) { + this.hostConfig.setExtraHosts(extraHosts); + return this; + } + + @Override + public CreateContainerCmd withHostConfig(HostConfig hostConfig) { + checkNotNull(hostConfig, "no host config was specified"); + this.hostConfig = hostConfig; + return this; + } + + @Override + public CreateContainerCmdImpl withHostName(String hostName) { + this.hostName = hostName; + return this; + } + + @Override + public CreateContainerCmdImpl withImage(String image) { + this.image = image; + return this; + } + + @Override + public CreateContainerCmdImpl withLinks(Link... links) { + checkNotNull(links, "links was not specified"); + this.hostConfig.setLinks(links); + return this; + } + + @Override + public CreateContainerCmd withLxcConf(LxcConf... lxcConf) { + checkNotNull(lxcConf, "lxcConf was not specified"); + this.hostConfig.setLxcConf(lxcConf); + return this; + } + + @Override + public CreateContainerCmdImpl withMacAddress(String macAddress) { + this.macAddress = macAddress; + return this; + } + + @Override + public CreateContainerCmdImpl withMemoryLimit(long memoryLimit) { + this.memoryLimit = memoryLimit; + return this; + } + + @Override + public CreateContainerCmdImpl withMemorySwap(long memorySwap) { + this.memorySwap = memorySwap; + return this; + } + + @Override + public CreateContainerCmdImpl withName(String name) { + checkNotNull(name, "name was not specified"); + this.name = name; + return this; + } + + @Override + public CreateContainerCmd withNetworkMode(String networkMode) { + checkNotNull(networkMode, "networkMode was not specified"); + this.hostConfig.setNetworkMode(networkMode); + return this; + } + + @Override + public CreateContainerCmd withPortBindings(PortBinding... portBindings) { + checkNotNull(portBindings, "portBindings was not specified"); + this.hostConfig.setPortBindings(new Ports(portBindings)); + return this; + } + + @Override + public CreateContainerCmd withPortBindings(Ports portBindings) { + checkNotNull(portBindings, "portBindings was not specified"); + this.hostConfig.setPortBindings(portBindings); + return this; + } + + @Override + public CreateContainerCmdImpl withPortSpecs(String... portSpecs) { + this.portSpecs = portSpecs; + return this; + } + + @Override + public CreateContainerCmd withPrivileged(boolean privileged) { + this.hostConfig.setPrivileged(privileged); + return this; + } + + @Override + public CreateContainerCmd withPublishAllPorts(boolean publishAllPorts) { + this.hostConfig.setPublishAllPorts(publishAllPorts); + return this; + } + + @Override + public CreateContainerCmd withRestartPolicy(RestartPolicy restartPolicy) { + this.hostConfig.setRestartPolicy(restartPolicy); + return this; + } + + @Override + public CreateContainerCmdImpl withStdInOnce(boolean stdInOnce) { + this.stdInOnce = stdInOnce; + return this; + } + + @Override + public CreateContainerCmdImpl withStdinOpen(boolean stdinOpen) { + this.stdinOpen = stdinOpen; + return this; + } + + @Override + public CreateContainerCmdImpl withTty(boolean tty) { + this.tty = tty; + return this; + } + + @Override + public CreateContainerCmdImpl withUser(String user) { + this.user = user; + return this; + } + + @Override + public CreateContainerCmdImpl withVolumes(Volume... volumes) { + this.volumes = new Volumes(volumes); + return this; + } + + @Override + public CreateContainerCmdImpl withVolumesFrom(VolumesFrom... volumesFrom) { + this.hostConfig.setVolumesFrom(volumesFrom); + return this; + } + + @Override + public CreateContainerCmdImpl withWorkingDir(String workingDir) { + this.workingDir = workingDir; + return this; + } + +} diff --git a/src/main/java/com/github/dockerjava/core/command/CreateImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/CreateImageCmdImpl.java new file mode 100644 index 00000000..daa883f9 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/CreateImageCmdImpl.java @@ -0,0 +1,83 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; + +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.github.dockerjava.api.command.CreateImageCmd; +import com.github.dockerjava.api.command.CreateImageResponse; + +/** + * Create an image by importing the given stream of a tar file. + */ +public class CreateImageCmdImpl extends AbstrDockerCmd implements CreateImageCmd { + + private String repository, tag; + + private InputStream imageStream; + + /** + * @param repository the repository to import to + * @param imageStream the InputStream of the tar file + */ + public CreateImageCmdImpl(CreateImageCmd.Exec exec, String repository, InputStream imageStream) { + super(exec); + withRepository(repository); + withImageStream(imageStream); + } + + @Override + public String getRepository() { + return repository; + } + + @Override + public String getTag() { + return tag; + } + + @Override + public InputStream getImageStream() { + return imageStream; + } + + /** + * @param repository the repository to import to + */ + @Override + public CreateImageCmdImpl withRepository(String repository) { + checkNotNull(repository, "repository was not specified"); + this.repository = repository; + return this; + } + + /** + * @param imageStream the InputStream of the tar file + */ + @Override + public CreateImageCmdImpl withImageStream(InputStream imageStream) { + checkNotNull(imageStream, "imageStream was not specified"); + this.imageStream = imageStream; + return this; + } + + /** + * @param tag any tag for this image + */ + @Override + public CreateImageCmdImpl withTag(String tag) { + checkNotNull(tag, "tag was not specified"); + this.tag = tag; + return this; + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("import - ") + .append(repository != null ? repository + ":" : "") + .append(tag != null ? tag : "") + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/EventStreamReader.java b/src/main/java/com/github/dockerjava/core/command/EventStreamReader.java new file mode 100644 index 00000000..b8975224 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/EventStreamReader.java @@ -0,0 +1,35 @@ +package com.github.dockerjava.core.command; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.io.InputStream; + +public class EventStreamReader implements AutoCloseable { + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final Class type; + private final InputStream inputStream; + + public EventStreamReader(InputStream inputStream, Class type) { + this.inputStream = inputStream; + this.type = type; + } + + public I readItem() throws IOException { + try { + return objectMapper.readValue(inputStream, type); + } catch (IOException e) { + // dirty, but works + if (e.getMessage().equals("Stream closed")) { + return null; + } + throw e; + } + } + + @Override + public void close() throws IOException { + inputStream.close(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java new file mode 100644 index 00000000..ac55de71 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/EventsCmdImpl.java @@ -0,0 +1,67 @@ +package com.github.dockerjava.core.command; + +import java.util.concurrent.ExecutorService; + +import com.github.dockerjava.api.command.EventCallback; +import com.github.dockerjava.api.command.EventsCmd; + +/** + * Stream docker events + */ +public class EventsCmdImpl extends AbstrDockerCmd implements EventsCmd { + + private String since; + private String until; + private EventCallback eventCallback; + + public EventsCmdImpl(EventsCmd.Exec exec, EventCallback eventCallback) { + super(exec); + withEventCallback(eventCallback); + } + + @Override + public EventsCmd withSince(String since) { + this.since = since; + return this; + } + + @Override + public EventsCmd withUntil(String until) { + this.until = until; + return this; + } + + @Override + public EventsCmd withEventCallback(EventCallback eventCallback) { + this.eventCallback = eventCallback; + return this; + } + + @Override + public String getSince() { + return since; + } + + @Override + public String getUntil() { + return until; + } + + @Override + public EventCallback getEventCallback() { + return eventCallback; + } + + @Override + public ExecutorService exec() { + return super.exec(); + } + + @Override + public String toString() { + return new StringBuilder("events") + .append(since != null ? " --since=" + since : "") + .append(until != null ? " --until=" + until : "") + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java new file mode 100644 index 00000000..65b86ed9 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java @@ -0,0 +1,125 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.ExecCreateCmd; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; + +public class ExecCreateCmdImpl extends AbstrDockerCmd implements ExecCreateCmd { + + private String containerId; + + @JsonProperty("AttachStdin") + private boolean attachStdin; + + @JsonProperty("AttachStdout") + private boolean attachStdout; + + @JsonProperty("AttachStderr") + private boolean attachStderr; + + @JsonProperty("Tty") + private boolean tty; + + @JsonProperty("Cmd") + private String[] cmd; + + public ExecCreateCmdImpl(ExecCreateCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public ExecCreateCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public ExecCreateCmd withAttachStdin(boolean attachStdin) { + this.attachStdin = attachStdin; + return this; + } + + @Override + public ExecCreateCmd withAttachStdin() { + return withAttachStdin(true); + } + + @Override + public ExecCreateCmd withAttachStdout(boolean attachStdout) { + this.attachStdout = attachStdout; + return this; + } + + @Override + public ExecCreateCmd withAttachStdout() { + return withAttachStdout(true); + } + + @Override + public ExecCreateCmd withAttachStderr(boolean attachStderr) { + this.attachStderr = attachStderr; + return this; + } + + @Override + public ExecCreateCmd withAttachStderr() { + return withAttachStderr(true); + } + + @Override + public ExecCreateCmd withTty(boolean tty) { + this.tty = tty; + return this; + } + + @Override + public ExecCreateCmd withTty() { + return withTty(true); + } + + @Override + public ExecCreateCmd withCmd(String... cmd) { + this.cmd = cmd; + return this; + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public boolean hasAttachStdinEnabled() { + return attachStdin; + } + + @Override + public boolean hasAttachStdoutEnabled() { + return attachStdout; + } + + @Override + public boolean hasAttachStderrEnabled() { + return attachStderr; + } + + @Override + public boolean hasTtyEnabled() { + return tty; + } + + /** + * @throws NotFoundException No such container + */ + @Override + public ExecCreateCmdResponse exec() throws NotFoundException { + return super.exec(); + } + + +} diff --git a/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java new file mode 100644 index 00000000..193a2cb6 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/ExecStartCmdImpl.java @@ -0,0 +1,73 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.ExecStartCmd; + +public class ExecStartCmdImpl extends AbstrDockerCmd implements ExecStartCmd { + + private String execId; + + private boolean detach, tty; + + public ExecStartCmdImpl(ExecStartCmd.Exec exec, String execId) { + super(exec); + withExecId(execId); + } + + @Override + public String getExecId() { + return execId; + } + + @Override + public ExecStartCmd withExecId(String execId) { + checkNotNull(execId, "execId was not specified"); + this.execId = execId; + return this; + } + + @Override + public boolean hasDetachEnabled() { + return detach; + } + + @Override + public boolean hasTtyEnabled() { + return tty; + } + + @Override + public ExecStartCmd withDetach(boolean detach) { + this.detach = detach; + return this; + } + + @Override + public ExecStartCmd withTty(boolean tty) { + this.tty = tty; + return this; + } + + @Override + public ExecStartCmd withDetach() { + return withDetach(true); + } + + @Override + public ExecStartCmd withTty() { + return withTty(true); + } + + /** + * @throws com.github.dockerjava.api.NotFoundException No such exec instance + */ + @Override + public InputStream exec() throws NotFoundException { + return super.exec(); + } + +} diff --git a/src/main/java/com/github/dockerjava/core/command/FrameReader.java b/src/main/java/com/github/dockerjava/core/command/FrameReader.java new file mode 100644 index 00000000..c3979146 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/FrameReader.java @@ -0,0 +1,66 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.StreamType; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Breaks the input into frame. Similar to how a buffered reader would readLies. + *

+ * See: {@link }http://docs.docker.com/v1.6/reference/api/docker_remote_api_v1.13/#attach-to-a-container} + */ +public class FrameReader implements AutoCloseable { + + private static final int HEADER_SIZE = 8; + private final InputStream inputStream; + + public FrameReader(InputStream inputStream) { + this.inputStream = inputStream; + } + + private static StreamType streamType(byte streamType) { + switch (streamType) { + case 0: + return StreamType.STDIN; + case 1: + return StreamType.STDOUT; + case 2: + return StreamType.STDERR; + default: + throw new IllegalArgumentException("invalid streamType"); + } + } + + /** + * @return A frame, or null if no more frames. + */ + public Frame readFrame() throws IOException { + byte[] header = new byte[HEADER_SIZE]; + int headerSize = inputStream.read(header); + + if (headerSize == -1) { + return null; + } + + if (headerSize != HEADER_SIZE) { + throw new IOException(String.format("header must be %d bytes long, but was %d", HEADER_SIZE, headerSize)); + } + + int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8) + (header[7] & 0xff); + + byte[] payload = new byte[payloadSize]; + int actualPayloadSize = inputStream.read(payload); + if (actualPayloadSize != payloadSize) { + throw new IOException(String.format("payload must be %d bytes long, but was %d", payloadSize, actualPayloadSize)); + } + + return new Frame(streamType(header[0]), payload); + } + + @Override + public void close() throws IOException { + inputStream.close(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/InfoCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/InfoCmdImpl.java new file mode 100644 index 00000000..767da204 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/InfoCmdImpl.java @@ -0,0 +1,19 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.command.InfoCmd; +import com.github.dockerjava.api.model.Info; + +/** + * Return Docker server info + */ +public class InfoCmdImpl extends AbstrDockerCmd implements InfoCmd { + + public InfoCmdImpl(InfoCmd.Exec exec) { + super(exec); + } + + @Override + public String toString() { + return "info"; + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/InspectContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/InspectContainerCmdImpl.java new file mode 100644 index 00000000..9215f233 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/InspectContainerCmdImpl.java @@ -0,0 +1,45 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.InspectContainerCmd; +import com.github.dockerjava.api.command.InspectContainerResponse; + +/** + * Inspect the details of a container. + */ +public class InspectContainerCmdImpl extends AbstrDockerCmd implements InspectContainerCmd { + + private String containerId; + + public InspectContainerCmdImpl(InspectContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public InspectContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public String toString() { + return "inspect " + containerId; + } + + /** + * @throws NotFoundException No such container + */ + @Override + public InspectContainerResponse exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/InspectExecCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/InspectExecCmdImpl.java new file mode 100644 index 00000000..1fd72862 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/InspectExecCmdImpl.java @@ -0,0 +1,41 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.DockerCmdExec; +import com.github.dockerjava.api.command.InspectExecCmd; +import com.github.dockerjava.api.command.InspectExecResponse; +import com.google.common.base.Preconditions; + +public class InspectExecCmdImpl extends AbstrDockerCmd implements InspectExecCmd { + private String execId; + + public InspectExecCmdImpl(InspectExecCmd.Exec execution, String execId) { + super(execution); + withExecId(execId); + } + + @Override + public String getExecId() { + return execId; + } + + @Override + public InspectExecCmd withExecId(String execId) { + Preconditions.checkNotNull(execId, "execId was not specified"); + this.execId = execId; + return this; + } + + @Override + public String toString() { + return "inspect " + execId; + } + + /** + * @throws NotFoundException No such exec + */ + @Override + public InspectExecResponse exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/InspectImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/InspectImageCmdImpl.java new file mode 100644 index 00000000..0b477e80 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/InspectImageCmdImpl.java @@ -0,0 +1,46 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.InspectImageCmd; +import com.github.dockerjava.api.command.InspectImageResponse; + + +/** + * Inspect the details of an image. + */ +public class InspectImageCmdImpl extends AbstrDockerCmd implements InspectImageCmd { + + private String imageId; + + public InspectImageCmdImpl(InspectImageCmd.Exec exec, String imageId) { + super(exec); + withImageId(imageId); + } + + @Override + public String getImageId() { + return imageId; + } + + @Override + public InspectImageCmd withImageId(String imageId) { + checkNotNull(imageId, "imageId was not specified"); + this.imageId = imageId; + return this; + } + + @Override + public String toString() { + return "inspect " + imageId; + } + + /** + * @throws NotFoundException No such image + */ + @Override + public InspectImageResponse exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/KillContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/KillContainerCmdImpl.java new file mode 100644 index 00000000..c6175a8a --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/KillContainerCmdImpl.java @@ -0,0 +1,57 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.KillContainerCmd; + + +/** + * Kill a running container. + */ +public class KillContainerCmdImpl extends AbstrDockerCmd implements KillContainerCmd { + + private String containerId, signal; + + public KillContainerCmdImpl(KillContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public String getSignal() { + return signal; + } + + @Override + public KillContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public KillContainerCmd withSignal(String signal) { + checkNotNull(signal, "signal was not specified"); + this.signal = signal; + return this; + } + + @Override + public String toString() { + return "kill " + containerId; + } + + /** + * @throws NotFoundException No such container + */ + @Override + public Void exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/ListContainersCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/ListContainersCmdImpl.java new file mode 100644 index 00000000..7144e767 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/ListContainersCmdImpl.java @@ -0,0 +1,102 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; + +import com.github.dockerjava.api.command.ListContainersCmd; +import com.github.dockerjava.api.model.Container; + + +/** + * List containers + * + * @param showAll - true or false, Show all containers. Only running containers are shown by default. + * @param showSize - true or false, Show the containers sizes. This is false by default. + * @param limit - Show `limit` last created containers, include non-running ones. There is no limit by default. + * @param sinceId - Show only containers created since Id, include non-running ones. + * @param beforeId - Show only containers created before Id, include non-running ones. + * + */ +public class ListContainersCmdImpl extends AbstrDockerCmd> implements ListContainersCmd { + + private int limit = -1; + + private boolean showSize, showAll = false; + + private String sinceId, beforeId; + + public ListContainersCmdImpl(ListContainersCmd.Exec exec) { + super(exec); + } + + @Override + public int getLimit() { + return limit; + } + + @Override + public boolean hasShowSizeEnabled() { + return showSize; + } + + @Override + public boolean hasShowAllEnabled() { + return showAll; + } + + @Override + public String getSinceId() { + return sinceId; + } + + @Override + public String getBeforeId() { + return beforeId; + } + + @Override + public ListContainersCmd withShowAll(boolean showAll) { + this.showAll = showAll; + return this; + } + + @Override + public ListContainersCmd withShowSize(boolean showSize) { + this.showSize = showSize; + return this; + } + + @Override + public ListContainersCmd withLimit(int limit) { + checkArgument(limit > 0, "limit must be greater 0"); + this.limit = limit; + return this; + } + + @Override + public ListContainersCmd withSince(String since) { + checkNotNull(since, "since was not specified"); + this.sinceId = since; + return this; + } + + @Override + public ListContainersCmd withBefore(String before) { + checkNotNull(before, "before was not specified"); + this.beforeId = before; + return this; + } + + @Override + public String toString() { + return new StringBuilder("ps ") + .append(showAll ? "--all=true" : "") + .append(showSize ? "--size=true" : "") + .append(sinceId != null ? "--since " + sinceId : "") + .append(beforeId != null ? "--before " + beforeId : "") + .append(limit != -1 ? "-n " + limit : "") + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/ListImagesCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/ListImagesCmdImpl.java new file mode 100644 index 00000000..998dded5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/ListImagesCmdImpl.java @@ -0,0 +1,56 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; + +import com.github.dockerjava.api.command.ListImagesCmd; +import com.github.dockerjava.api.model.Image; + +/** + * List images + * + * @param showAll - Show all images (by default filter out the intermediate images used to build) + * @param filters - a json encoded value of the filters (a map[string][]string) to process on the images list. + */ +public class ListImagesCmdImpl extends AbstrDockerCmd> implements ListImagesCmd { + + private String filters; + + private boolean showAll = false; + + public ListImagesCmdImpl(ListImagesCmd.Exec exec) { + super(exec); + } + + @Override + public String getFilters() { + return filters; + } + + @Override + public boolean hasShowAllEnabled() { + return showAll; + } + + @Override + public ListImagesCmd withShowAll(boolean showAll) { + this.showAll = showAll; + return this; + } + + @Override + public ListImagesCmd withFilters(String filter) { + checkNotNull(filter, "filters have not been specified"); + this.filters = filter; + return this; + } + + @Override + public String toString() { + return new StringBuilder("images ") + .append(showAll ? "--all=true" : "") + .append(filters != null ? "--filter " + filters : "") + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/LogContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/LogContainerCmdImpl.java new file mode 100644 index 00000000..d405a802 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/LogContainerCmdImpl.java @@ -0,0 +1,148 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.LogContainerCmd; + +/** + * Get container logs + * + * @param followStream + * - true or false, return stream. Defaults to false. + * @param stdout + * - true or false, includes stdout log. Defaults to false. + * @param stderr + * - true or false, includes stderr log. Defaults to false. + * @param timestamps + * - true or false, if true, print timestamps for every log line. + * Defaults to false. + * @param tail + * - `all` or ``, Output specified number of lines at the end of logs + */ +public class LogContainerCmdImpl extends AbstrDockerCmd implements LogContainerCmd { + + private String containerId; + + private int tail = -1; + + private boolean followStream, timestamps, stdout, stderr; + + public LogContainerCmdImpl(LogContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public int getTail() { + return tail; + } + + @Override + public boolean hasFollowStreamEnabled() { + return followStream; + } + + @Override + public boolean hasTimestampsEnabled() { + return timestamps; + } + + @Override + public boolean hasStdoutEnabled() { + return stdout; + } + + @Override + public boolean hasStderrEnabled() { + return stderr; + } + + @Override + public LogContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public LogContainerCmd withFollowStream() { + return withFollowStream(true); + } + + @Override + public LogContainerCmd withFollowStream(boolean followStream) { + this.followStream = followStream; + return this; + } + + @Override + public LogContainerCmd withTimestamps() { + return withTimestamps(true); + } + + @Override + public LogContainerCmd withTimestamps(boolean timestamps) { + this.timestamps = timestamps; + return this; + } + + @Override + public LogContainerCmd withStdOut() { + return withStdOut(true); + } + + @Override + public LogContainerCmd withStdOut(boolean stdout) { + this.stdout = stdout; + return this; + } + + @Override + public LogContainerCmd withStdErr() { + return withStdErr(true); + } + + @Override + public LogContainerCmd withStdErr(boolean stderr) { + this.stderr = stderr; + return this; + } + + @Override + public LogContainerCmd withTailAll() { + this.tail = -1; + return this; + } + + + @Override + public LogContainerCmd withTail(int tail) { + this.tail = tail; + return this; + } + + @Override + public String toString() { + return new StringBuilder("logs ") + .append(followStream ? "--follow=true" : "") + .append(timestamps ? "--timestamps=true" : "") + .append(containerId) + .toString(); + } + + /** + * @throws NotFoundException No such container + */ + @Override + public InputStream exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/PauseContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/PauseContainerCmdImpl.java new file mode 100644 index 00000000..5ea60fe8 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/PauseContainerCmdImpl.java @@ -0,0 +1,49 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.PauseContainerCmd; + +/** + * Pause a container. + * + * @param containerId - Id of the container + * + */ +public class PauseContainerCmdImpl extends AbstrDockerCmd implements PauseContainerCmd { + + private String containerId; + + public PauseContainerCmdImpl(PauseContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public PauseContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public String toString() { + return new StringBuilder("pause ") + .append(containerId) + .toString(); + } + + /** + * @throws NotFoundException No such container + */ + @Override + public Void exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/PingCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/PingCmdImpl.java new file mode 100644 index 00000000..15cc1f5b --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/PingCmdImpl.java @@ -0,0 +1,14 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.command.PingCmd; + +/** + * Ping the Docker server + * + */ +public class PingCmdImpl extends AbstrDockerCmd implements PingCmd { + + public PingCmdImpl(PingCmd.Exec exec) { + super(exec); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/PullImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/PullImageCmdImpl.java new file mode 100644 index 00000000..d1d4ee02 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/PullImageCmdImpl.java @@ -0,0 +1,67 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; + +import com.github.dockerjava.api.command.PullImageCmd; +import com.github.dockerjava.api.model.AuthConfig; + +/** + * + * Pull image from repository. + * + */ +public class PullImageCmdImpl extends AbstrAuthCfgDockerCmd implements PullImageCmd { + + private String repository, tag, registry; + + public PullImageCmdImpl(PullImageCmd.Exec exec, AuthConfig authConfig, String repository) { + super(exec, authConfig); + withRepository(repository); + } + + @Override + public String getRepository() { + return repository; + } + + @Override + public String getTag() { + return tag; + } + + @Override + public String getRegistry() { + return registry; + } + + @Override + public PullImageCmd withRepository(String repository) { + checkNotNull(repository, "repository was not specified"); + this.repository = repository; + return this; + } + + @Override + public PullImageCmd withTag(String tag) { + checkNotNull(tag, "tag was not specified"); + this.tag = tag; + return this; + } + + @Override + public PullImageCmd withRegistry(String registry) { + checkNotNull(registry, "registry was not specified"); + this.registry = registry; + return this; + } + + @Override + public String toString() { + return new StringBuilder("pull ") + .append(repository) + .append(tag != null ? ":" + tag : "") + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/PushImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/PushImageCmdImpl.java new file mode 100644 index 00000000..a775bffd --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/PushImageCmdImpl.java @@ -0,0 +1,67 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.PushImageCmd; + +/** + * 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 class PushImageCmdImpl extends AbstrAuthCfgDockerCmd implements PushImageCmd { + + private String name; + private String tag; + + public PushImageCmdImpl(PushImageCmd.Exec exec, String name) { + super(exec); + withName(name); + } + + @Override + public String getName() { + return name; + } + + @Override + public String getTag() { + return tag; + } + + /** + * @param name The name, e.g. "alexec/busybox" or just "busybox" if you want to default. Not null. + */ + @Override + public PushImageCmd withName(String name) { + checkNotNull(name, "name was not specified"); + this.name = name; + return this; + } + + /** + * @param tag The image's tag. Can be null or empty. + */ + @Override + public PushImageCmd withTag(String tag) { + checkNotNull(tag, "tag was not specified"); + this.tag = tag; + return this; + } + + @Override + public String toString() { + return new StringBuilder("push ") + .append(name) + .toString(); + } + + /** + * @throws NotFoundException No such image + */ + @Override + public Response exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/RemoveContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/RemoveContainerCmdImpl.java new file mode 100644 index 00000000..7cd06395 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/RemoveContainerCmdImpl.java @@ -0,0 +1,79 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.RemoveContainerCmd; + +/** + * Remove a container. + * + * @param removeVolumes - true or false, Remove the volumes associated to the container. Defaults to false + * @param force - true or false, Removes the container even if it was running. Defaults to false + */ +public class RemoveContainerCmdImpl extends AbstrDockerCmd implements RemoveContainerCmd { + + private String containerId; + + private boolean removeVolumes, force; + + public RemoveContainerCmdImpl(RemoveContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public boolean hasRemoveVolumesEnabled() { + return removeVolumes; + } + + @Override + public boolean hasForceEnabled() { + return force; + } + + @Override + public RemoveContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public RemoveContainerCmd withRemoveVolumes(boolean removeVolumes) { + this.removeVolumes = removeVolumes; + return this; + } + + @Override + public RemoveContainerCmd withForce() { + return withForce(true); + } + + @Override + public RemoveContainerCmd withForce(boolean force) { + this.force = force; + return this; + } + + @Override + public String toString() { + return new StringBuilder("rm ") + .append(removeVolumes ? "--volumes=true" : "") + .append(force ? "--force=true" : "").append(containerId) + .toString(); + } + + /** + * @throws NotFoundException No such container + */ + @Override + public Void exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/RemoveImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/RemoveImageCmdImpl.java new file mode 100644 index 00000000..87e096aa --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/RemoveImageCmdImpl.java @@ -0,0 +1,84 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.RemoveImageCmd; + +/** + * + * Remove an image, deleting any tags it might have. + * + */ +public class RemoveImageCmdImpl extends AbstrDockerCmd implements RemoveImageCmd { + + private String imageId; + + private boolean force, noPrune; + + public RemoveImageCmdImpl(RemoveImageCmd.Exec exec, String imageId) { + super(exec); + withImageId(imageId); + } + + @Override + public String getImageId() { + return imageId; + } + + @Override + public boolean hasForceEnabled() { + return force; + } + + @Override + public boolean hasNoPruneEnabled() { + return noPrune; + } + + @Override + public RemoveImageCmd withImageId(String imageId) { + checkNotNull(imageId, "imageId was not specified"); + this.imageId = imageId; + return this; + } + + @Override + public RemoveImageCmd withForce() { + return withForce(true); + } + + @Override + public RemoveImageCmd withForce(boolean force) { + this.force = force; + return this; + } + + @Override + public RemoveImageCmd withNoPrune() { + return withNoPrune(true); + } + + @Override + public RemoveImageCmd withNoPrune(boolean noPrune) { + this.noPrune = noPrune; + return this; + } + + @Override + public String toString() { + return new StringBuilder("rmi ") + .append(noPrune ? "--no-prune=true" : "") + .append(force ? "--force=true" : "") + .append(imageId) + .toString(); + } + + /** + * @throws NotFoundException No such image + */ + @Override + public Void exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/RestartContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/RestartContainerCmdImpl.java new file mode 100644 index 00000000..adcac9b4 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/RestartContainerCmdImpl.java @@ -0,0 +1,65 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.RestartContainerCmd; + +/** + * Restart a running container. + * + * @param timeout - Timeout in seconds before killing the container. Defaults to 10 seconds. + * + */ +public class RestartContainerCmdImpl extends AbstrDockerCmd implements RestartContainerCmd { + + private String containerId; + + private int timeout = 10; + + public RestartContainerCmdImpl(RestartContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public int getTimeout() { + return timeout; + } + + @Override + public RestartContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public RestartContainerCmd withtTimeout(int timeout) { + checkArgument(timeout >= 0, "timeout must be greater or equal 0"); + this.timeout = timeout; + return this; + } + + @Override + public String toString() { + return new StringBuilder("restart ") + .append("--time=" + timeout + " ") + .append(containerId) + .toString(); + } + + /** + * @throws NotFoundException No such container + */ + @Override + public Void exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/SaveImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/SaveImageCmdImpl.java new file mode 100644 index 00000000..30bb13b2 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/SaveImageCmdImpl.java @@ -0,0 +1,63 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.SaveImageCmd; + +import java.io.InputStream; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class SaveImageCmdImpl extends AbstrDockerCmd implements SaveImageCmd { + private String name; + private String tag; + + public SaveImageCmdImpl(SaveImageCmd.Exec exec, String name) { + super(exec); + withName(name); + } + + @Override + public String getName() { + return name; + } + + @Override + public String getTag() { + return tag; + } + + /** + * @param name The name, e.g. "alexec/busybox" or just "busybox" if you want to default. Not null. + */ + @Override + public SaveImageCmd withName(String name) { + checkNotNull(name, "name was not specified"); + this.name = name; + return this; + } + + /** + * @param tag The image's tag. Can be null or empty. + */ + @Override + public SaveImageCmd withTag(String tag) { + checkNotNull(tag, "tag was not specified"); + this.tag = tag; + return this; + } + + @Override + public String toString() { + return new StringBuilder("get ") + .append(name) + .toString(); + } + + /** + * @throws com.github.dockerjava.api.NotFoundException No such image + */ + @Override + public InputStream exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/SearchImagesCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/SearchImagesCmdImpl.java new file mode 100644 index 00000000..82c94e49 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/SearchImagesCmdImpl.java @@ -0,0 +1,43 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; + +import com.github.dockerjava.api.command.SearchImagesCmd; +import com.github.dockerjava.api.model.SearchItem; + +/** + * Search images + * + * @param term - search term + * + */ +public class SearchImagesCmdImpl extends AbstrDockerCmd> implements SearchImagesCmd { + + private String term; + + public SearchImagesCmdImpl(SearchImagesCmd.Exec exec, String term) { + super(exec); + withTerm(term); + } + + @Override + public String getTerm() { + return term; + } + + @Override + public SearchImagesCmd withTerm(String term) { + checkNotNull(term, "term was not specified"); + this.term = term; + return this; + } + + @Override + public String toString() { + return new StringBuilder("search ") + .append(term) + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/StartContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/StartContainerCmdImpl.java new file mode 100644 index 00000000..dba35fb9 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/StartContainerCmdImpl.java @@ -0,0 +1,304 @@ +package com.github.dockerjava.core.command; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.commons.lang.builder.ToStringBuilder; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.NotModifiedException; +import com.github.dockerjava.api.command.StartContainerCmd; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Binds; +import com.github.dockerjava.api.model.Capability; +import com.github.dockerjava.api.model.Device; +import com.github.dockerjava.api.model.Link; +import com.github.dockerjava.api.model.Links; +import com.github.dockerjava.api.model.LxcConf; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.api.model.RestartPolicy; + + +/** + * Start a container + */ +@JsonInclude(NON_EMPTY) +public class StartContainerCmdImpl extends AbstrDockerCmd implements StartContainerCmd { + + @JsonIgnore + private String containerId; + + @JsonProperty("Binds") + private Binds binds; + + @JsonProperty("Links") + private Links links; + + @JsonProperty("LxcConf") + private LxcConf[] lxcConf; + + @JsonProperty("PortBindings") + private Ports portBindings; + + @JsonProperty("PublishAllPorts") + private Boolean publishAllPorts; + + @JsonProperty("Privileged") + private Boolean privileged; + + @JsonProperty("Dns") + private String[] dns; + + @JsonProperty("DnsSearch") + private String[] dnsSearch; + + @JsonProperty("VolumesFrom") + private String volumesFrom; + + @JsonProperty("NetworkMode") + private String networkMode; + + @JsonProperty("Devices") + private Device[] devices; + + @JsonProperty("ExtraHosts") + private String[] extraHosts; + + @JsonProperty("RestartPolicy") + private RestartPolicy restartPolicy; + + @JsonProperty("CapAdd") + private Capability[] capAdd; + + @JsonProperty("CapDrop") + private Capability[] capDrop; + + public StartContainerCmdImpl(StartContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + @JsonIgnore + public Bind[] getBinds() { + return (binds == null) ? new Bind[0] : binds.getBinds(); + } + + @Override + @JsonIgnore + public Link[] getLinks() { + return (links == null) ? new Link[0] : links.getLinks(); + } + + @Override + public LxcConf[] getLxcConf() { + return lxcConf; + } + + @Override + public Ports getPortBindings() { + return portBindings; + } + + @Override + public Boolean isPublishAllPorts() { + return publishAllPorts; + } + + @Override + public Boolean isPrivileged() { + return privileged; + } + + @Override + public String[] getDns() { + return dns; + } + + @Override + public String[] getDnsSearch() { + return dnsSearch; + } + + @Override + public String getVolumesFrom() { + return volumesFrom; + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public String getNetworkMode() { + return networkMode; + } + + @Override + public Device[] getDevices() { + return devices; + } + + @Override + public String[] getExtraHosts() { + return extraHosts; + } + + @Override + public RestartPolicy getRestartPolicy() { + return restartPolicy; + } + + @Override + public Capability[] getCapAdd() { + return capAdd; + } + + @Override + public Capability[] getCapDrop() { + return capDrop; + } + + @Override + @JsonIgnore + public StartContainerCmd withBinds(Bind... binds) { + checkNotNull(binds, "binds was not specified"); + this.binds = new Binds(binds); + return this; + } + + @Override + @JsonIgnore + public StartContainerCmd withLinks(Link... links) { + checkNotNull(links, "links was not specified"); + this.links = new Links(links); + return this; + } + + @Override + public StartContainerCmd withLxcConf(LxcConf... lxcConf) { + checkNotNull(lxcConf, "lxcConf was not specified"); + this.lxcConf = lxcConf; + return this; + } + + @Override + public StartContainerCmd withPortBindings(Ports portBindings) { + checkNotNull(portBindings, + "portBindings was not specified"); + this.portBindings = portBindings; + return this; + } + + @Override + public StartContainerCmd withPortBindings(PortBinding... portBindings) { + checkNotNull(portBindings, "portBindings was not specified"); + if (this.portBindings == null) { + this.portBindings = new Ports(); + } + this.portBindings.add(portBindings); + return this; + } + + @Override + public StartContainerCmd withPrivileged(Boolean privileged) { + this.privileged = privileged; + return this; + } + + @Override + public StartContainerCmd withPublishAllPorts(Boolean publishAllPorts) { + this.publishAllPorts = publishAllPorts; + return this; + } + + @Override + public StartContainerCmd withDns(String... dns) { + checkNotNull(dns, "dns was not specified"); + this.dns = dns; + return this; + } + + @Override + public StartContainerCmd withDnsSearch(String... dnsSearch) { + checkNotNull(dnsSearch, "dnsSearch was not specified"); + this.dnsSearch = dnsSearch; + return this; + } + + @Override + public StartContainerCmd withVolumesFrom(String volumesFrom) { + checkNotNull(volumesFrom, "volumesFrom was not specified"); + this.volumesFrom = volumesFrom; + return this; + } + + @Override + public StartContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public StartContainerCmd withNetworkMode(String networkMode) { + checkNotNull(networkMode, "networkMode was not specified"); + this.networkMode = networkMode; + return this; + } + + @Override + public StartContainerCmd withDevices(Device... devices) { + checkNotNull(devices, "devices was not specified"); + this.devices = devices; + return this; + } + + @Override + public StartContainerCmd withExtraHosts(String... extraHosts) { + checkNotNull(extraHosts, "extraHosts was not specified"); + this.extraHosts = extraHosts; + return this; + } + + + @Override + public StartContainerCmd withRestartPolicy(RestartPolicy restartPolicy) { + checkNotNull(restartPolicy, "restartPolicy was not specified"); + this.restartPolicy = restartPolicy; + return this; + } + + @Override + public StartContainerCmd withCapAdd(Capability... capAdd) { + checkNotNull(capAdd, "capAdd was not specified"); + this.capAdd = capAdd; + return this; + } + + @Override + public StartContainerCmd withCapDrop(Capability... capDrop) { + checkNotNull(capDrop, "capDrop was not specified"); + this.capDrop = capDrop; + return this; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this).toString(); + } + + /** + * @throws NotFoundException No such container + * @throws NotModifiedException Container already started + */ + @Override + public Void exec() throws NotFoundException, NotModifiedException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/StopContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/StopContainerCmdImpl.java new file mode 100644 index 00000000..e961bf8b --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/StopContainerCmdImpl.java @@ -0,0 +1,68 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.NotModifiedException; +import com.github.dockerjava.api.command.StopContainerCmd; + +/** + * Stop a running container. + * + * @param containerId - Id of the container + * @param timeout - Timeout in seconds before killing the container. Defaults to 10 seconds. + * + */ +public class StopContainerCmdImpl extends AbstrDockerCmd implements StopContainerCmd { + + private String containerId; + + private int timeout = 10; + + public StopContainerCmdImpl(StopContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public int getTimeout() { + return timeout; + } + + @Override + public StopContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public StopContainerCmd withTimeout(int timeout) { + checkArgument(timeout >= 0, "timeout must be greater or equal 0"); + this.timeout = timeout; + return this; + } + + @Override + public String toString() { + return new StringBuilder("stop ") + .append("--time=" + timeout + " ") + .append(containerId) + .toString(); + } + + /** + * @throws NotFoundException No such container + * @throws NotModifiedException Container already stopped + */ + @Override + public Void exec() throws NotFoundException, NotModifiedException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/TagImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/TagImageCmdImpl.java new file mode 100644 index 00000000..8655ddfe --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/TagImageCmdImpl.java @@ -0,0 +1,90 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.command.TagImageCmd; + + +/** + * 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 force (not documented) + * + */ +public class TagImageCmdImpl extends AbstrDockerCmd implements TagImageCmd { + + private String imageId, repository, tag; + + private boolean force; + + public TagImageCmdImpl(TagImageCmd.Exec exec, String imageId, String repository, String tag) { + super(exec); + withImageId(imageId); + withRepository(repository); + withTag(tag); + } + + @Override + public String getImageId() { + return imageId; + } + + @Override + public String getRepository() { + return repository; + } + + @Override + public String getTag() { + return tag; + } + + @Override + public boolean hasForceEnabled() { + return force; + } + + @Override + public TagImageCmd withImageId(String imageId) { + checkNotNull(imageId, "imageId was not specified"); + this.imageId = imageId; + return this; + } + + @Override + public TagImageCmd withRepository(String repository) { + checkNotNull(repository, "repository was not specified"); + this.repository = repository; + return this; + } + + @Override + public TagImageCmd withTag(String tag) { + checkNotNull(tag, "tag was not specified"); + this.tag = tag; + return this; + } + + @Override + public TagImageCmd withForce() { + return withForce(true); + } + + @Override + public TagImageCmd withForce(boolean force) { + this.force = force; + return this; + } + + @Override + public String toString() { + return new StringBuilder("tag ") + .append(force ? "--force=true " : "") + .append(repository != null ? repository + "/" : "") + .append(imageId) + .append(tag != null ? ":" + tag : "") + .toString(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/TopContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/TopContainerCmdImpl.java new file mode 100644 index 00000000..5f7190fb --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/TopContainerCmdImpl.java @@ -0,0 +1,63 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.TopContainerCmd; +import com.github.dockerjava.api.command.TopContainerResponse; + +/** + * List processes running inside a container + */ +public class TopContainerCmdImpl extends AbstrDockerCmd implements TopContainerCmd { + + private String containerId; + + private String psArgs; + + public TopContainerCmdImpl(TopContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public String getPsArgs() { + return psArgs; + } + + @Override + public TopContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + + @Override + public TopContainerCmd withPsArgs(String psArgs) { + checkNotNull(psArgs, "psArgs was not specified"); + this.psArgs = psArgs; + return this; + } + + @Override + public String toString() { + return new StringBuilder("top ") + .append(containerId) + .append(psArgs != null ? " " + psArgs : "") + .toString(); + } + + /** + * @throws NotFoundException No such container + */ + @Override + public TopContainerResponse exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/UnpauseContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/UnpauseContainerCmdImpl.java new file mode 100644 index 00000000..92d715bc --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/UnpauseContainerCmdImpl.java @@ -0,0 +1,49 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.UnpauseContainerCmd; + +/** + * Unpause a container. + * + * @param containerId - Id of the container + * + */ +public class UnpauseContainerCmdImpl extends AbstrDockerCmd implements UnpauseContainerCmd { + + private String containerId; + + public UnpauseContainerCmdImpl(UnpauseContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public UnpauseContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public String toString() { + return new StringBuilder("pause ") + .append(containerId) + .toString(); + } + + /** + * @throws NotFoundException No such container + */ + @Override + public Void exec() throws NotFoundException { + return super.exec(); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/VersionCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/VersionCmdImpl.java new file mode 100644 index 00000000..211ed140 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/VersionCmdImpl.java @@ -0,0 +1,19 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.command.VersionCmd; +import com.github.dockerjava.api.model.Version; + +/** + * Returns the Docker version info. + */ +public class VersionCmdImpl extends AbstrDockerCmd implements VersionCmd { + + @Override + public String toString() { + return "version"; + } + + public VersionCmdImpl(VersionCmd.Exec exec) { + super(exec); + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/WaitContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/WaitContainerCmdImpl.java new file mode 100644 index 00000000..d6250cc6 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/WaitContainerCmdImpl.java @@ -0,0 +1,37 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.github.dockerjava.api.command.WaitContainerCmd; + +/** + * Wait a container + * + * Block until container stops, then returns its exit code + */ +public class WaitContainerCmdImpl extends AbstrDockerCmd implements WaitContainerCmd { + + private String containerId; + + public WaitContainerCmdImpl(WaitContainerCmd.Exec exec, String containerId) { + super(exec); + withContainerId(containerId); + } + + @Override + public String getContainerId() { + return containerId; + } + + @Override + public WaitContainerCmd withContainerId(String containerId) { + checkNotNull(containerId, "containerId was not specified"); + this.containerId = containerId; + return this; + } + + @Override + public String toString() { + return "wait " + containerId; + } +} diff --git a/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java b/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java new file mode 100644 index 00000000..802299a7 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java @@ -0,0 +1,243 @@ +package com.github.dockerjava.core.dockerfile; + +import com.github.dockerjava.api.DockerClientException; +import com.github.dockerjava.core.CompressArchiveUtil; +import com.github.dockerjava.core.FilePathUtil; +import com.github.dockerjava.core.GoLangFileMatch; +import com.github.dockerjava.core.GoLangFileMatchException; +import com.github.dockerjava.core.GoLangMatchFileFilter; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.filefilter.TrueFileFilter; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.collect.Collections2; + +/** + * Parse a Dockerfile. + */ +public class Dockerfile { + + public final File dockerFile; + + public Dockerfile(File dockerFile) { + + if (!dockerFile.exists()) { + throw new IllegalStateException( + String.format("Dockerfile %s does not exist", dockerFile.getAbsolutePath())); + } + + if (!dockerFile.isFile()) { + throw new IllegalStateException( + String.format("Dockerfile %s is not a file", dockerFile.getAbsolutePath())); + } + + this.dockerFile = dockerFile; + + } + + private static class LineTransformer + implements Function> { + + private int line = 0; + + @Override + public Optional apply(String input) { + try { + line++; + return DockerfileStatement.createFromLine(input); + + } catch (Exception ex) { + throw new DockerClientException("Error on dockerfile line " + line); + } + } + } + + public Iterable getStatements() throws IOException { + Collection dockerFileContent = FileUtils.readLines(dockerFile); + + if (dockerFileContent.size() <= 0) { + throw new DockerClientException(String.format( + "Dockerfile %s is empty", dockerFile)); + } + + Collection> optionals = Collections2 + .transform(dockerFileContent, new LineTransformer()); + + return Optional.presentInstances(optionals); + } + + public List getIgnores() throws IOException { + List ignores = new ArrayList(); + File dockerIgnoreFile = new File(getDockerFolder(), ".dockerignore"); + if (dockerIgnoreFile.exists()) { + int lineNumber = 0; + List dockerIgnoreFileContent = FileUtils.readLines(dockerIgnoreFile); + for (String pattern : dockerIgnoreFileContent) { + lineNumber++; + pattern = pattern.trim(); + if (pattern.isEmpty()) { + continue; // skip empty lines + } + pattern = FilenameUtils.normalize(pattern); + try { + // validate pattern and make sure we aren't excluding Dockerfile + if (GoLangFileMatch.match(pattern, "Dockerfile")) { + throw new DockerClientException( + String.format( + "Dockerfile is excluded by pattern '%s' on line %s in .dockerignore file", + pattern, lineNumber)); + } + ignores.add(pattern); + } catch (GoLangFileMatchException e) { + throw new DockerClientException(String.format( + "Invalid pattern '%s' on line %s in .dockerignore file", pattern, lineNumber)); + } + } + } + return ignores; + } + + + public ScannedResult parse() throws IOException { + return new ScannedResult(); + } + + + public File getDockerFolder() { + return dockerFile.getParentFile(); + } + + + /** + * Result of scanning / parsing a docker file. + */ + public class ScannedResult { + + final List ignores; + final Map environmentMap = new HashMap(); + final List filesToAdd = new ArrayList(); + + public InputStream buildDockerFolderTar() { + return buildDockerFolderTar(getDockerFolder()); + } + + public InputStream buildDockerFolderTar(File directory) { + + // ARCHIVE TAR + File dockerFolderTar = null; + + try { + String archiveNameWithOutExtension = UUID.randomUUID().toString(); + + dockerFolderTar = CompressArchiveUtil.archiveTARFiles(directory, + filesToAdd, + archiveNameWithOutExtension); + return FileUtils.openInputStream(dockerFolderTar); + + } catch (IOException ex) { + FileUtils.deleteQuietly(dockerFolderTar); + throw new DockerClientException( + "Error occurred while preparing Docker context folder.", ex); + } + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("ignores", ignores) + .add("environmentMap", environmentMap) + .add("filesToAdd", filesToAdd) + .toString(); + } + + public ScannedResult() throws IOException { + + ignores = getIgnores(); + filesToAdd.add(dockerFile); + + for (DockerfileStatement statement : getStatements()) { + if (statement instanceof DockerfileStatement.Env) { + processEnvStatement((DockerfileStatement.Env) statement); + } else if (statement instanceof DockerfileStatement.Add) { + processAddStatement((DockerfileStatement.Add) statement); + } + } + } + + private void processAddStatement(DockerfileStatement.Add add) throws IOException { + + add = add.transform(environmentMap); + + if (add.isFileResource()) { + + File dockerFolder = getDockerFolder(); + String resource = add.source; + + File src = new File(resource); + if (!src.isAbsolute()) { + src = new File(dockerFolder, resource); + } else { + throw new DockerClientException(String.format( + "Source file %s must be relative to %s", + src, dockerFolder)); + } + + if (src.exists()) { + src = src.getCanonicalFile(); + if (src.isDirectory()) { + Collection files = FileUtils.listFiles(src, new GoLangMatchFileFilter(src, ignores), TrueFileFilter.INSTANCE); + filesToAdd.addAll(files); + } else if (!GoLangFileMatch.match(ignores, FilePathUtil.relativize(dockerFolder, src))) { + filesToAdd.add(src); + } else { + throw new DockerClientException(String.format("Source file %s is excluded by .dockerignore file", src)); + } + } else { + filesToAdd.addAll(resolveWildcards(src, ignores)); + } + } + } + + private Collection resolveWildcards(File file, List ignores) { + List filesToAdd = new ArrayList(); + + File parent = file.getParentFile(); + if (parent != null) { + if (parent.isDirectory()) { + Collection files = FileUtils.listFiles(parent, + new GoLangMatchFileFilter(parent, ignores), + TrueFileFilter.INSTANCE); + filesToAdd.addAll(files); + } else { + filesToAdd.addAll(resolveWildcards(parent, ignores)); + } + } else { + throw new DockerClientException(String.format( + "Source file %s doesn't exist", file)); + } + + return filesToAdd; + } + + private void processEnvStatement(DockerfileStatement.Env env) { + + environmentMap.put(env.variable, env.value); + } + + } + +} diff --git a/src/main/java/com/github/dockerjava/core/dockerfile/DockerfileStatement.java b/src/main/java/com/github/dockerjava/core/dockerfile/DockerfileStatement.java new file mode 100644 index 00000000..c048f77d --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/dockerfile/DockerfileStatement.java @@ -0,0 +1,206 @@ +package com.github.dockerjava.core.dockerfile; + +import com.github.dockerjava.api.DockerClientException; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.common.base.Objects; +import com.google.common.base.Optional; + +/** + * A statement present in a dockerfile. + */ +public abstract class DockerfileStatement { + + private DockerfileStatement() { + } + + public T transform(Map env) { + return (T) this; + } + + protected String filterForEnvironmentVars(Map environmentMap, + String extractedResource) { + + if (environmentMap.size() > 0) { + + String currentResourceContent = extractedResource; + + for (Map.Entry entry : environmentMap.entrySet()) { + + String variable = entry.getKey(); + + String replacementValue = entry.getValue(); + + // handle: $VARIABLE case + currentResourceContent = currentResourceContent.replaceAll( + "\\$" + variable, Matcher.quoteReplacement(replacementValue)); + + // handle ${VARIABLE} case + currentResourceContent = currentResourceContent.replaceAll( + "\\$\\{" + variable + "\\}", Matcher.quoteReplacement(replacementValue)); + + } + + return currentResourceContent; + } else { + return extractedResource; + } + } + + + /** + * A statement that we don't particularly care about. + */ + public static class OtherLine extends DockerfileStatement { + + public final String statement; + + public OtherLine(String statement) { + this.statement = statement; + } + + @Override + public String toString() { + return statement; + } + } + + /** + * An ADD or a COPY + */ + public static class Add extends DockerfileStatement { + + private static final Pattern ADD_OR_COPY_PATTERN = Pattern + .compile("^(ADD|COPY)\\s+(.*)\\s+(.*)$"); + + public final String source; + public final String destination; + + private Add(String source, String destination) { + this.source = source; + this.destination = destination; + } + + private Add(final Matcher matcher) { + source = matcher.group(2); + destination = matcher.group(3); + } + + @Override + public Add transform(Map env) { + String resource = filterForEnvironmentVars(env, source).trim(); + return new Add(resource, destination); + } + + public boolean isFileResource() { + URI uri; + try { + uri = new URI(source); + } catch (URISyntaxException e) { + return false; + } + return uri.getScheme() == null || "file".equals(uri.getScheme()); + } + + /** + * Createa an Add if it matches, or missing if not. + * + * @param statement statement that may be an ADD or a COPY + * @return optional typed item. + */ + public static Optional create(String statement) { + Matcher matcher = ADD_OR_COPY_PATTERN.matcher(statement.trim()); + if (!matcher.find()) { + return Optional.absent(); + } + + if (matcher.groupCount() != 3) { + throw new DockerClientException("Wrong ADD or COPY format"); + } + + return Optional.of(new Add(matcher)); + } + + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("source", source) + .add("destination", destination) + .toString(); + } + } + + public static class Env extends DockerfileStatement { + + private static final Pattern ENV_PATTERN = Pattern + .compile("^ENV\\s+(.*)\\s+(.*)$"); + + public final String variable; + public final String value; + + private Env(String variable, String value) { + this.variable = variable; + this.value = value; + } + + private Env(Matcher envMatcher) { + this.variable = envMatcher.group(1).trim(); + this.value = envMatcher.group(2).trim(); + } + + public static Optional create(String statement) { + Matcher matcher = ENV_PATTERN.matcher(statement.trim()); + if (!matcher.find()) { + return Optional.absent(); + } + + if (matcher.groupCount() != 2) { + throw new DockerClientException("Wrong ENV format"); + } + + return Optional.of(new Env(matcher)); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("variable", variable) + .add("value", value) + .toString(); + } + } + + /** + * Return a dockerfile statement + */ + public static Optional createFromLine(String cmd) { + if (cmd.trim().isEmpty() || cmd.startsWith("#")) { + return Optional.absent(); + } + + Optional line; + + line = Add.create(cmd); + + if (line.isPresent()) { + return line; + } + + line = Env.create(cmd); + + if (line.isPresent()) { + return line; + } + + return Optional.of(new OtherLine(cmd)); + + + } + +} diff --git a/src/main/java/com/github/dockerjava/core/util/FollowRedirectsFilter.java b/src/main/java/com/github/dockerjava/core/util/FollowRedirectsFilter.java new file mode 100644 index 00000000..b4431b1f --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/util/FollowRedirectsFilter.java @@ -0,0 +1,37 @@ +package com.github.dockerjava.core.util; + +import java.io.IOException; +import java.io.InputStream; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.client.ClientResponseFilter; +import javax.ws.rs.core.Response; + +/** + * Default implementation of RedirectStrategy honors the restrictions + * on automatic redirection of entity enclosing methods such as POST + * and PUT imposed by the HTTP specification. 302 Moved Temporarily, + * 301 Moved Permanently and 307 Temporary Redirect status codes will + * result in an automatic redirect of HEAD and GET methods only. + * + * {@link org.apache.http.impl.client.DefaultRedirectStrategy} + * + * This filter allows arbitrary redirection for other methods. + */ +public class FollowRedirectsFilter implements ClientResponseFilter { + + @Override + public void filter(ClientRequestContext requestContext, + ClientResponseContext responseContext) throws IOException { + if (!responseContext.getStatusInfo().getFamily().equals(Response.Status.Family.REDIRECTION)) { + return; + } + + Response resp = requestContext.getClient().target(responseContext.getLocation()) + .request().method(requestContext.getMethod()); + responseContext.setEntityStream((InputStream) resp.getEntity()); + responseContext.setStatusInfo(resp.getStatusInfo()); + responseContext.setStatus(resp.getStatus()); + } +} diff --git a/src/main/java/com/github/dockerjava/core/util/JsonClientFilter.java b/src/main/java/com/github/dockerjava/core/util/JsonClientFilter.java new file mode 100644 index 00000000..f2911d96 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/util/JsonClientFilter.java @@ -0,0 +1,25 @@ +package com.github.dockerjava.core.util; + + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.client.ClientResponseFilter; +import javax.ws.rs.core.MediaType; +import java.io.IOException; + +/** + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + * + */ +public class JsonClientFilter implements ClientResponseFilter { + + + @Override + public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { + if (responseContext.getMediaType() != null && responseContext.getMediaType().isCompatible(MediaType.TEXT_PLAIN_TYPE)) { + String newContentType = "application/json" + responseContext.getMediaType().toString().substring(10); + responseContext.getHeaders().putSingle("Content-Type", newContentType); + } + } +} diff --git a/src/main/java/com/github/dockerjava/core/util/LoggingFilter.java b/src/main/java/com/github/dockerjava/core/util/LoggingFilter.java new file mode 100644 index 00000000..5a72ba23 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/util/LoggingFilter.java @@ -0,0 +1,331 @@ +package com.github.dockerjava.core.util; + +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2011-2014 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.client.ClientResponseFilter; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.WriterInterceptor; +import javax.ws.rs.ext.WriterInterceptorContext; +import javax.annotation.Priority; + + +/** + * Universal logging filter. + * + * Can be used on client or server side. Has the highest priority. + * + * @author Pavel Bucek (pavel.bucek at oracle.com) + * @author Martin Matula (martin.matula at oracle.com) + */ +@PreMatching +@Priority(Integer.MIN_VALUE) +@SuppressWarnings("ClassWithMultipleLoggers") +public class LoggingFilter implements ContainerRequestFilter, ClientRequestFilter, ContainerResponseFilter, + ClientResponseFilter, WriterInterceptor { + + private static final Logger LOGGER = LoggerFactory.getLogger(LoggingFilter.class.getName()); + private static final String NOTIFICATION_PREFIX = "* "; + private static final String REQUEST_PREFIX = "> "; + private static final String RESPONSE_PREFIX = "< "; + private static final String ENTITY_LOGGER_PROPERTY = LoggingFilter.class.getName() + ".entityLogger"; + + private static final Comparator>> COMPARATOR = + new Comparator>>() { + + @Override + public int compare(final Map.Entry> o1, final Map.Entry> o2) { + return o1.getKey().compareToIgnoreCase(o2.getKey()); + } + }; + + private static final int DEFAULT_MAX_ENTITY_SIZE = 8 * 1024; + + // + @SuppressWarnings("NonConstantLogger") + private final Logger logger; + private final AtomicLong _id = new AtomicLong(0); + private final boolean printEntity; + private final int maxEntitySize; + + /** + * Create a logging filter logging the request and response to a default JDK + * logger, named as the fully qualified class name of this class. Entity + * logging is turned off by default. + */ + public LoggingFilter() { + this(LOGGER, false); + } + + /** + * Create a logging filter with custom logger and custom settings of entity + * logging. + * + * @param logger the logger to log requests and responses. + * @param printEntity if true, entity will be logged as well up to the default maxEntitySize, which is 8KB + */ + @SuppressWarnings("BooleanParameter") + public LoggingFilter(final Logger logger, final boolean printEntity) { + this.logger = logger; + this.printEntity = printEntity; + this.maxEntitySize = DEFAULT_MAX_ENTITY_SIZE; + } + + /** + * Creates a logging filter with custom logger and entity logging turned on, but potentially limiting the size + * of entity to be buffered and logged. + * + * @param logger the logger to log requests and responses. + * @param maxEntitySize maximum number of entity bytes to be logged (and buffered) - if the entity is larger, + * logging filter will print (and buffer in memory) only the specified number of bytes + * and print "...more..." string at the end. + */ + public LoggingFilter(final Logger logger, final int maxEntitySize) { + this.logger = logger; + this.printEntity = true; + this.maxEntitySize = maxEntitySize; + } + + private void log(final StringBuilder b) { + if (logger != null) { + logger.debug(b.toString()); + } + } + + private StringBuilder prefixId(final StringBuilder b, final long id) { + b.append(Long.toString(id)).append(" "); + return b; + } + + private void printRequestLine(final StringBuilder b, final String note, final long id, final String method, final URI uri) { + prefixId(b, id).append(NOTIFICATION_PREFIX) + .append(note) + .append(" on thread ").append(Thread.currentThread().getName()) + .append("\n"); + prefixId(b, id).append(REQUEST_PREFIX).append(method).append(" "). + append(uri.toASCIIString()).append("\n"); + } + + private void printResponseLine(final StringBuilder b, final String note, final long id, final int status) { + prefixId(b, id).append(NOTIFICATION_PREFIX) + .append(note) + .append(" on thread ").append(Thread.currentThread().getName()).append("\n"); + prefixId(b, id).append(RESPONSE_PREFIX). + append(Integer.toString(status)). + append("\n"); + } + + private void printPrefixedHeaders(final StringBuilder b, final long id, final String prefix, final MultivaluedMap headers) { + for (final Map.Entry> headerEntry : getSortedHeaders(headers.entrySet())) { + final List val = headerEntry.getValue(); + final String header = headerEntry.getKey(); + + if (val.size() == 1) { + prefixId(b, id).append(prefix).append(header).append(": ").append(val.get(0)).append("\n"); + } else { + final StringBuilder sb = new StringBuilder(); + boolean add = false; + for (final Object s : val) { + if (add) { + sb.append(','); + } + add = true; + sb.append(s); + } + prefixId(b, id).append(prefix).append(header).append(": ").append(sb.toString()).append("\n"); + } + } + } + + private Set>> getSortedHeaders(final Set>> headers) { + final TreeSet>> sortedHeaders = new TreeSet>>(COMPARATOR); + sortedHeaders.addAll(headers); + return sortedHeaders; + } + + private InputStream logInboundEntity(final StringBuilder b, InputStream stream) throws IOException { + if (!stream.markSupported()) { + stream = new BufferedInputStream(stream); + } + stream.mark(maxEntitySize + 1); + final byte[] entity = new byte[maxEntitySize + 1]; + final int entitySize = Math.max(0, stream.read(entity)); + b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize))); + if (entitySize > maxEntitySize) { + b.append("...more..."); + } + b.append('\n'); + stream.reset(); + return stream; + } + + @Override + public void filter(final ClientRequestContext context) throws IOException { + final long id = this._id.incrementAndGet(); + final StringBuilder b = new StringBuilder(); + + printRequestLine(b, "Sending client request", id, context.getMethod(), context.getUri()); + printPrefixedHeaders(b, id, REQUEST_PREFIX, context.getStringHeaders()); + + if (printEntity && context.hasEntity()) { + final OutputStream stream = new LoggingStream(b, context.getEntityStream()); + context.setEntityStream(stream); + context.setProperty(ENTITY_LOGGER_PROPERTY, stream); + // not calling log(b) here - it will be called by the interceptor + } else { + log(b); + } + } + + @Override + public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) throws IOException { + final long id = this._id.incrementAndGet(); + final StringBuilder b = new StringBuilder(); + + printResponseLine(b, "Client response received", id, responseContext.getStatus()); + printPrefixedHeaders(b, id, RESPONSE_PREFIX, responseContext.getHeaders()); + + if (printEntity && responseContext.hasEntity()) { + responseContext.setEntityStream(logInboundEntity(b, responseContext.getEntityStream())); + } + + log(b); + } + + @Override + public void filter(final ContainerRequestContext context) throws IOException { + final long id = this._id.incrementAndGet(); + final StringBuilder b = new StringBuilder(); + + printRequestLine(b, "Server has received a request", id, context.getMethod(), context.getUriInfo().getRequestUri()); + printPrefixedHeaders(b, id, REQUEST_PREFIX, context.getHeaders()); + + if (printEntity && context.hasEntity()) { + context.setEntityStream(logInboundEntity(b, context.getEntityStream())); + } + + log(b); + } + + @Override + public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) throws IOException { + final long id = this._id.incrementAndGet(); + final StringBuilder b = new StringBuilder(); + + printResponseLine(b, "Server responded with a response", id, responseContext.getStatus()); + printPrefixedHeaders(b, id, RESPONSE_PREFIX, responseContext.getStringHeaders()); + + if (printEntity && responseContext.hasEntity()) { + final OutputStream stream = new LoggingStream(b, responseContext.getEntityStream()); + responseContext.setEntityStream(stream); + requestContext.setProperty(ENTITY_LOGGER_PROPERTY, stream); + // not calling log(b) here - it will be called by the interceptor + } else { + log(b); + } + } + + @Override + public void aroundWriteTo(final WriterInterceptorContext writerInterceptorContext) throws IOException, WebApplicationException { + final LoggingStream stream = (LoggingStream) writerInterceptorContext.getProperty(ENTITY_LOGGER_PROPERTY); + writerInterceptorContext.proceed(); + if (stream != null) { + log(stream.getStringBuilder()); + } + } + + private class LoggingStream extends OutputStream { + private final StringBuilder b; + private final OutputStream inner; + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + LoggingStream(final StringBuilder b, final OutputStream inner) { + this.b = b; + this.inner = inner; + } + + StringBuilder getStringBuilder() { + // write entity to the builder + final byte[] entity = baos.toByteArray(); + + b.append(new String(entity, 0, Math.min(entity.length, maxEntitySize))); + if (entity.length > maxEntitySize) { + b.append("...more..."); + } + b.append('\n'); + + return b; + } + + @Override + public void write(final int i) throws IOException { + if (baos.size() <= maxEntitySize) { + baos.write(i); + } + inner.write(i); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/core/util/ResponseStatusExceptionFilter.java b/src/main/java/com/github/dockerjava/core/util/ResponseStatusExceptionFilter.java new file mode 100644 index 00000000..7e970856 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/util/ResponseStatusExceptionFilter.java @@ -0,0 +1,96 @@ +package com.github.dockerjava.core.util; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.client.ClientResponseFilter; +import javax.ws.rs.core.MediaType; + +import org.apache.commons.io.IOUtils; + +import com.github.dockerjava.api.BadRequestException; +import com.github.dockerjava.api.ConflictException; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.InternalServerErrorException; +import com.github.dockerjava.api.NotAcceptableException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.NotModifiedException; +import com.github.dockerjava.api.UnauthorizedException; + +/** + * This {@link ClientResponseFilter} implementation detects http status codes and throws {@link DockerException}s + * + * @author marcus + * + */ +public class ResponseStatusExceptionFilter implements ClientResponseFilter { + + + @Override + public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { + int status = responseContext.getStatus(); + switch (status) { + case 200: + case 201: + case 204: + return; + case 304: + throw new NotModifiedException(getBodyAsMessage(responseContext)); + case 400: + throw new BadRequestException(getBodyAsMessage(responseContext)); + case 401: + throw new UnauthorizedException(getBodyAsMessage(responseContext)); + case 404: + throw new NotFoundException(getBodyAsMessage(responseContext)); + case 406: + throw new NotAcceptableException(getBodyAsMessage(responseContext)); + case 409: + throw new ConflictException(getBodyAsMessage(responseContext)); + case 500: + throw new InternalServerErrorException(getBodyAsMessage(responseContext)); + default: + throw new DockerException(getBodyAsMessage(responseContext), status); + } + } + + private String getBodyAsMessage(ClientResponseContext responseContext) + throws IOException { + if (responseContext.hasEntity()) { + int contentLength = responseContext.getLength(); + if (contentLength != -1) { + byte[] buffer = new byte[contentLength]; + try { + InputStream entityStream = responseContext.getEntityStream(); + IOUtils.readFully(entityStream, buffer); + entityStream.close(); + } + catch (EOFException e) { + return null; + } + Charset charset = null; + MediaType mediaType = responseContext.getMediaType(); + if (mediaType != null) { + String charsetName = mediaType.getParameters().get("charset"); + if (charsetName != null) { + try { + charset = Charset.forName(charsetName); + } + catch (Exception e) { + //Do noting... + } + } + } + if (charset == null) { + charset = Charset.defaultCharset(); + } + String message = new String(buffer, charset); + return message; + } + } + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/core/util/SelectiveLoggingFilter.java b/src/main/java/com/github/dockerjava/core/util/SelectiveLoggingFilter.java new file mode 100644 index 00000000..8cfe88b7 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/util/SelectiveLoggingFilter.java @@ -0,0 +1,48 @@ +package com.github.dockerjava.core.util; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; + + +/** + * A version of the logging filter that will avoid trying to log entities which can cause + * issues with the console. + * + * @author sfitts + * + */ +public class SelectiveLoggingFilter extends LoggingFilter { + + // Immutable'ish + private static final Set SKIPPED_CONTENT; + static { + Set s = new HashSet(); + s.add(MediaType.APPLICATION_OCTET_STREAM); + s.add("application/tar"); + SKIPPED_CONTENT = Collections.unmodifiableSet(s); + } + + + public SelectiveLoggingFilter(Logger logger, boolean b) { + super(logger, b); + } + + @Override + public void filter(ClientRequestContext context) throws IOException { + // Unless the content type is in the list of those we want to ellide, then just have + // our super-class handle things. + Object contentType = context.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); + if (contentType == null || !SKIPPED_CONTENT.contains(contentType.toString())) { + super.filter(context); + } + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/AbstrDockerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/AbstrDockerCmdExec.java new file mode 100644 index 00000000..84039510 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/AbstrDockerCmdExec.java @@ -0,0 +1,77 @@ +package com.github.dockerjava.jaxrs; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.WebTarget; + +import org.apache.commons.codec.binary.Base64; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.DockerCmd; +import com.github.dockerjava.api.command.DockerCmdExec; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.AuthConfigurations; + +public abstract class AbstrDockerCmdExec, RES_T> + implements DockerCmdExec { + + private WebTarget baseResource; + + public AbstrDockerCmdExec(WebTarget baseResource) { + checkNotNull(baseResource, + "baseResource was not specified"); + this.baseResource = baseResource; + } + + protected WebTarget getBaseResource() { + return baseResource; + } + + protected String registryAuth(AuthConfig authConfig) { + try { + return Base64.encodeBase64String(new ObjectMapper() + .writeValueAsString(authConfig).getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected String registryConfigs(AuthConfigurations authConfigs) { + try { + return Base64.encodeBase64String(new ObjectMapper() + .writeValueAsString(authConfigs).getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public RES_T exec(CMD_T command) { + // this hack works because of ResponseStatusExceptionFilter + RES_T result; + try { + result = execute(command); + + } catch (ProcessingException e) { + if(e.getCause() instanceof DockerException) { + throw (DockerException)e.getCause(); + } else { + throw e; + } + } finally { + try { + command.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return result; + } + + protected abstract RES_T execute(CMD_T command); +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/ApacheUnixSocket.java b/src/main/java/com/github/dockerjava/jaxrs/ApacheUnixSocket.java new file mode 100644 index 00000000..23d6d1ab --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/ApacheUnixSocket.java @@ -0,0 +1,326 @@ +package com.github.dockerjava.jaxrs; +/* + * Copyright (c) 2014 Spotify AB. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.SocketChannel; +import java.util.ArrayDeque; +import java.util.Queue; + +import org.newsclub.net.unix.AFUNIXSocket; + +/** + * Provides a socket that wraps an org.newsclub.net.unix.AFUNIXSocket and delays setting options + * until the socket is connected. This is necessary because the Apache HTTP client attempts to + * set options prior to connecting the socket, which doesn't work for Unix sockets since options + * are being set on the underlying file descriptor. Until the socket is connected, the file + * descriptor doesn't exist. + * + * This class also noop's any calls to setReuseAddress, which is called by the Apache client but + * isn't supported by AFUnixSocket. + */ +public class ApacheUnixSocket extends Socket { + + private final AFUNIXSocket inner; + + private final Queue optionsToSet = new ArrayDeque(); + + public ApacheUnixSocket() throws IOException { + this.inner = AFUNIXSocket.newInstance(); + } + + @Override + public void connect(final SocketAddress endpoint) throws IOException { + inner.connect(endpoint); + setAllSocketOptions(); + } + + @Override + public void connect(final SocketAddress endpoint, final int timeout) throws IOException { + inner.connect(endpoint, timeout); + setAllSocketOptions(); + } + + @Override + public void bind(final SocketAddress bindpoint) throws IOException { + inner.bind(bindpoint); + setAllSocketOptions(); + } + + @Override + public InetAddress getInetAddress() { + return inner.getInetAddress(); + } + + @Override + public InetAddress getLocalAddress() { + return inner.getLocalAddress(); + } + + @Override + public int getPort() { + return inner.getPort(); + } + + @Override + public int getLocalPort() { + return inner.getLocalPort(); + } + + @Override + public SocketAddress getRemoteSocketAddress() { + return inner.getRemoteSocketAddress(); + } + + @Override + public SocketAddress getLocalSocketAddress() { + return inner.getLocalSocketAddress(); + } + + @Override + public SocketChannel getChannel() { + return inner.getChannel(); + } + + @Override + public InputStream getInputStream() throws IOException { + return inner.getInputStream(); + } + + @Override + public OutputStream getOutputStream() throws IOException { + return inner.getOutputStream(); + } + + private void setSocketOption(final SocketOptionSetter s) throws SocketException { + if (inner.isConnected()) { + s.run(); + } else { + if (!optionsToSet.offer(s)) { + throw new SocketException("Failed to queue option"); + } + } + } + + private void setAllSocketOptions() throws SocketException { + for (SocketOptionSetter s : optionsToSet) { + s.run(); + } + } + + @Override + public void setTcpNoDelay(final boolean on) throws SocketException { + setSocketOption(new SocketOptionSetter() { + @Override + public void run() throws SocketException { + inner.setTcpNoDelay(on); + } + }); + } + + @Override + public boolean getTcpNoDelay() throws SocketException { + return inner.getTcpNoDelay(); + } + + @Override + public void setSoLinger(final boolean on, final int linger) throws SocketException { + setSocketOption(new SocketOptionSetter() { + @Override + public void run() throws SocketException { + inner.setSoLinger(on, linger); + } + }); + } + + @Override + public int getSoLinger() throws SocketException { + return inner.getSoLinger(); + } + + @Override + public void sendUrgentData(final int data) throws IOException { + inner.sendUrgentData(data); + } + + @Override + public void setOOBInline(final boolean on) throws SocketException { + setSocketOption(new SocketOptionSetter() { + @Override + public void run() throws SocketException { + inner.setOOBInline(on); + } + }); + } + + @Override + public boolean getOOBInline() throws SocketException { + return inner.getOOBInline(); + } + + @Override + public synchronized void setSoTimeout(final int timeout) throws SocketException { + setSocketOption(new SocketOptionSetter() { + @Override + public void run() throws SocketException { + inner.setSoTimeout(timeout); + } + }); + } + + @Override + public synchronized int getSoTimeout() throws SocketException { + return inner.getSoTimeout(); + } + + @Override + public synchronized void setSendBufferSize(final int size) throws SocketException { + setSocketOption(new SocketOptionSetter() { + @Override + public void run() throws SocketException { + inner.setSendBufferSize(size); + } + }); + } + + @Override + public synchronized int getSendBufferSize() throws SocketException { + return inner.getSendBufferSize(); + } + + @Override + public synchronized void setReceiveBufferSize(final int size) throws SocketException { + setSocketOption(new SocketOptionSetter() { + @Override + public void run() throws SocketException { + inner.setReceiveBufferSize(size); + } + }); + } + + @Override + public synchronized int getReceiveBufferSize() throws SocketException { + return inner.getReceiveBufferSize(); + } + + @Override + public void setKeepAlive(final boolean on) throws SocketException { + setSocketOption(new SocketOptionSetter() { + @Override + public void run() throws SocketException { + inner.setKeepAlive(on); + } + }); + } + + @Override + public boolean getKeepAlive() throws SocketException { + return inner.getKeepAlive(); + } + + @Override + public void setTrafficClass(final int tc) throws SocketException { + setSocketOption(new SocketOptionSetter() { + @Override + public void run() throws SocketException { + inner.setTrafficClass(tc); + } + }); + } + + @Override + public int getTrafficClass() throws SocketException { + return inner.getTrafficClass(); + } + + @Override + public void setReuseAddress(final boolean on) throws SocketException { + // not supported: Apache client tries to set it, but we want to just ignore it + } + + @Override + public boolean getReuseAddress() throws SocketException { + return inner.getReuseAddress(); + } + + @Override + public synchronized void close() throws IOException { + inner.close(); + } + + @Override + public void shutdownInput() throws IOException { + inner.shutdownInput(); + } + + @Override + public void shutdownOutput() throws IOException { + inner.shutdownOutput(); + } + + @Override + public String toString() { + return inner.toString(); + } + + @Override + public boolean isConnected() { + return inner.isConnected(); + } + + @Override + public boolean isBound() { + return inner.isBound(); + } + + @Override + public boolean isClosed() { + return inner.isClosed(); + } + + @Override + public boolean isInputShutdown() { + return inner.isInputShutdown(); + } + + @Override + public boolean isOutputShutdown() { + return inner.isOutputShutdown(); + } + + @Override + public void setPerformancePreferences(final int connectionTime, final int latency, + final int bandwidth) { + inner.setPerformancePreferences(connectionTime, latency, bandwidth); + } + + interface SocketOptionSetter { + void run() throws SocketException; + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/AttachContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/AttachContainerCmdExec.java new file mode 100644 index 00000000..de4bb3f0 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/AttachContainerCmdExec.java @@ -0,0 +1,47 @@ +package com.github.dockerjava.jaxrs; + +import java.io.InputStream; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.AttachContainerCmd; +import com.github.dockerjava.jaxrs.util.WrappedResponseInputStream; + +public class AttachContainerCmdExec extends + AbstrDockerCmdExec implements + AttachContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(AttachContainerCmdExec.class); + + public AttachContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected InputStream execute(AttachContainerCmd command) { + WebTarget webResource = getBaseResource() + .path("/containers/{id}/attach") + .resolveTemplate("id", command.getContainerId()) + .queryParam("logs", command.hasLogsEnabled() ? "1" : "0") + // .queryParam("stdin", command.hasStdinEnabled() ? "1" : "0") + .queryParam("stdout", command.hasStdoutEnabled() ? "1" : "0") + .queryParam("stderr", command.hasStderrEnabled() ? "1" : "0") + .queryParam("stream", + command.hasFollowStreamEnabled() ? "1" : "0"); + + LOGGER.trace("POST: {}", webResource); + + Response response = webResource.request() + .accept(MediaType.APPLICATION_OCTET_STREAM_TYPE) + .post(null, Response.class); + + return new WrappedResponseInputStream(response); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/AuthCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/AuthCmdExec.java new file mode 100644 index 00000000..3552d974 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/AuthCmdExec.java @@ -0,0 +1,39 @@ +package com.github.dockerjava.jaxrs; + +import com.github.dockerjava.api.UnauthorizedException; +import com.github.dockerjava.api.command.AuthCmd; +import com.github.dockerjava.api.model.AuthResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import static javax.ws.rs.client.Entity.entity; + +public class AuthCmdExec extends AbstrDockerCmdExec implements AuthCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(AuthCmdExec.class); + + public AuthCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected AuthResponse execute(AuthCmd command) { + WebTarget webResource = getBaseResource().path("/auth"); + LOGGER.trace("POST: {}", webResource); + Response response = webResource + .request() + .accept(MediaType.APPLICATION_JSON).post(entity(command.getAuthConfig(), MediaType.APPLICATION_JSON)); + + if(response.getStatus() == 401) { + throw new UnauthorizedException("Unauthorized"); + } + + return response.readEntity(AuthResponse.class); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/BuildImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/BuildImageCmdExec.java new file mode 100644 index 00000000..3bdfb006 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/BuildImageCmdExec.java @@ -0,0 +1,120 @@ +package com.github.dockerjava.jaxrs; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.github.dockerjava.api.command.BuildImageCmd; +import com.github.dockerjava.api.model.AuthConfigurations; +import com.github.dockerjava.api.model.EventStreamItem; +import com.github.dockerjava.jaxrs.util.WrappedResponseInputStream; +import com.google.common.collect.ImmutableList; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.RequestEntityProcessing; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; + +import static javax.ws.rs.client.Entity.entity; + +public class BuildImageCmdExec extends + AbstrDockerCmdExec implements + BuildImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(BuildImageCmdExec.class); + + public BuildImageCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected ResponseImpl execute(BuildImageCmd command) { + WebTarget webResource = getBaseResource().path("/build"); + String dockerFilePath = command.getPathToDockerfile(); + + if (command.getTag() != null) { + webResource = webResource.queryParam("t", command.getTag()); + } + if (command.hasNoCacheEnabled()) { + webResource = webResource.queryParam("nocache", "true"); + } + if (!command.hasRemoveEnabled()) { + webResource = webResource.queryParam("rm", "false"); + } + if (command.isQuiet()) { + webResource = webResource.queryParam("q", "true"); + } + if (command.hasPullEnabled()) { + webResource = webResource.queryParam("pull", "true"); + } + if (dockerFilePath != null && !"Dockerfile".equals(dockerFilePath)) { + webResource = webResource.queryParam("dockerfile", dockerFilePath); + } + + webResource.property(ClientProperties.REQUEST_ENTITY_PROCESSING, + RequestEntityProcessing.CHUNKED); + webResource.property(ClientProperties.CHUNKED_ENCODING_SIZE, + 1024 * 1024); + + LOGGER.debug("POST: {}", webResource); + Response response = resourceWithOptionalAuthConfig(command, + webResource.request()) + .accept(MediaType.TEXT_PLAIN) + .post(entity(command.getTarInputStream(), "application/tar"), + Response.class); + + return new ResponseImpl(new WrappedResponseInputStream(response)); + + } + + private Invocation.Builder resourceWithOptionalAuthConfig( + BuildImageCmd command, Invocation.Builder request) { + AuthConfigurations authConfigs = command.getBuildAuthConfigs(); + if (authConfigs != null) { + request = request.header("X-Registry-Config", + registryConfigs(authConfigs)); + } + return request; + } + + public static class ResponseImpl extends BuildImageCmd.Response { + + private final InputStream proxy; + + public ResponseImpl(InputStream proxy) { + this.proxy = proxy; + } + + @Override + public Iterable getItems() throws IOException { + ObjectMapper mapper = new ObjectMapper(); + // we'll be reading instances of MyBean + ObjectReader reader = mapper.reader(EventStreamItem.class); + // and then do other configuration, if any, and read: + Iterator items = reader.readValues(proxy); + + try { + return ImmutableList.copyOf(items); + } finally { + proxy.close(); + } + } + + @Override + public int read() throws IOException { + return proxy.read(); + } + + @Override + public void close() throws IOException { + proxy.close(); + super.close(); + } + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/CommitCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/CommitCmdExec.java new file mode 100644 index 00000000..f1f4fe33 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/CommitCmdExec.java @@ -0,0 +1,38 @@ +package com.github.dockerjava.jaxrs; + +import static javax.ws.rs.client.Entity.entity; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.dockerjava.api.command.CommitCmd; + +public class CommitCmdExec extends AbstrDockerCmdExec implements CommitCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(CommitCmdExec.class); + + public CommitCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected String execute(CommitCmd command) { + WebTarget webResource = getBaseResource().path("/commit") + .queryParam("container", command.getContainerId()) + .queryParam("repo", command.getRepository()) + .queryParam("tag", command.getTag()) + .queryParam("m", command.getMessage()) + .queryParam("author", command.getAuthor()) + .queryParam("pause", command.hasPauseEnabled() ? "1" : "0"); + + LOGGER.trace("POST: {}", webResource); + ObjectNode objectNode = webResource.request().accept("application/vnd.docker.raw-stream").post(entity(command, MediaType.APPLICATION_JSON), ObjectNode.class); + return objectNode.get("Id").asText(); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/ContainerDiffCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/ContainerDiffCmdExec.java new file mode 100644 index 00000000..2b7059b3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/ContainerDiffCmdExec.java @@ -0,0 +1,34 @@ +package com.github.dockerjava.jaxrs; + +import java.util.List; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.model.ChangeLog; + +public class ContainerDiffCmdExec extends AbstrDockerCmdExec> implements ContainerDiffCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(ContainerDiffCmdExec.class); + + public ContainerDiffCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected List execute(ContainerDiffCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/changes").resolveTemplate("id", command.getContainerId()); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON) + .get(new GenericType>() { + }); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/CopyFileFromContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/CopyFileFromContainerCmdExec.java new file mode 100644 index 00000000..0a738be8 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/CopyFileFromContainerCmdExec.java @@ -0,0 +1,39 @@ +package com.github.dockerjava.jaxrs; + +import static javax.ws.rs.client.Entity.entity; + +import java.io.InputStream; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.CopyFileFromContainerCmd; +import com.github.dockerjava.jaxrs.util.WrappedResponseInputStream; + +public class CopyFileFromContainerCmdExec extends AbstrDockerCmdExec implements CopyFileFromContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(CopyFileFromContainerCmdExec.class); + + public CopyFileFromContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected InputStream execute(CopyFileFromContainerCmd command) { + WebTarget webResource = getBaseResource() + .path("/containers/{id}/copy") + .resolveTemplate("id", command.getContainerId()); + + LOGGER.trace("POST: " + webResource.toString()); + + Response response = webResource.request().accept(MediaType.APPLICATION_OCTET_STREAM_TYPE).post(entity(command, MediaType.APPLICATION_JSON)); + + return new WrappedResponseInputStream(response); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/CreateContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/CreateContainerCmdExec.java new file mode 100644 index 00000000..26dc25cd --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/CreateContainerCmdExec.java @@ -0,0 +1,35 @@ +package com.github.dockerjava.jaxrs; + +import static javax.ws.rs.client.Entity.entity; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.CreateContainerResponse; + +public class CreateContainerCmdExec extends AbstrDockerCmdExec implements CreateContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(CreateContainerCmdExec.class); + + public CreateContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected CreateContainerResponse execute(CreateContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/create"); + + if (command.getName() != null) { + webResource = webResource.queryParam("name", command.getName()); + } + + LOGGER.trace("POST: {} ", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON) + .post(entity(command, MediaType.APPLICATION_JSON), CreateContainerResponse.class); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/CreateImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/CreateImageCmdExec.java new file mode 100644 index 00000000..4c7bae8b --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/CreateImageCmdExec.java @@ -0,0 +1,35 @@ +package com.github.dockerjava.jaxrs; + +import static javax.ws.rs.client.Entity.entity; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.CreateImageCmd; +import com.github.dockerjava.api.command.CreateImageResponse; + +public class CreateImageCmdExec extends AbstrDockerCmdExec implements CreateImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(CreateImageCmdExec.class); + + public CreateImageCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected CreateImageResponse execute(CreateImageCmd command) { + WebTarget webResource = getBaseResource() + .path("/images/create") + .queryParam("repo", command.getRepository()) + .queryParam("tag", command.getTag()) + .queryParam("fromSrc", "-"); + + LOGGER.trace("POST: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_OCTET_STREAM_TYPE) + .post(entity(command.getImageStream(), MediaType.APPLICATION_OCTET_STREAM), CreateImageResponse.class); + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java new file mode 100644 index 00000000..ccacb1f7 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java @@ -0,0 +1,334 @@ +package com.github.dockerjava.jaxrs; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.net.URI; + +import javax.net.ssl.SSLContext; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; + +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.glassfish.jersey.CommonProperties; +import org.glassfish.jersey.apache.connector.ApacheClientProperties; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.HttpUrlConnectorProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.github.dockerjava.api.DockerClientException; +import com.github.dockerjava.api.command.AttachContainerCmd; +import com.github.dockerjava.api.command.AuthCmd; +import com.github.dockerjava.api.command.BuildImageCmd; +import com.github.dockerjava.api.command.CommitCmd; +import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.command.CopyFileFromContainerCmd; +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.CreateImageCmd; +import com.github.dockerjava.api.command.DockerCmdExecFactory; +import com.github.dockerjava.api.command.EventsCmd; +import com.github.dockerjava.api.command.ExecCreateCmd; +import com.github.dockerjava.api.command.ExecStartCmd; +import com.github.dockerjava.api.command.InfoCmd; +import com.github.dockerjava.api.command.InspectContainerCmd; +import com.github.dockerjava.api.command.InspectExecCmd; +import com.github.dockerjava.api.command.InspectImageCmd; +import com.github.dockerjava.api.command.KillContainerCmd; +import com.github.dockerjava.api.command.ListContainersCmd; +import com.github.dockerjava.api.command.ListImagesCmd; +import com.github.dockerjava.api.command.LogContainerCmd; +import com.github.dockerjava.api.command.PauseContainerCmd; +import com.github.dockerjava.api.command.PingCmd; +import com.github.dockerjava.api.command.PullImageCmd; +import com.github.dockerjava.api.command.PushImageCmd; +import com.github.dockerjava.api.command.RemoveContainerCmd; +import com.github.dockerjava.api.command.RemoveImageCmd; +import com.github.dockerjava.api.command.RestartContainerCmd; +import com.github.dockerjava.api.command.SaveImageCmd; +import com.github.dockerjava.api.command.SearchImagesCmd; +import com.github.dockerjava.api.command.StartContainerCmd; +import com.github.dockerjava.api.command.StatsCmd.Exec; +import com.github.dockerjava.api.command.StopContainerCmd; +import com.github.dockerjava.api.command.TagImageCmd; +import com.github.dockerjava.api.command.TopContainerCmd; +import com.github.dockerjava.api.command.UnpauseContainerCmd; +import com.github.dockerjava.api.command.VersionCmd; +import com.github.dockerjava.api.command.WaitContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.util.FollowRedirectsFilter; +import com.github.dockerjava.core.util.JsonClientFilter; +import com.github.dockerjava.core.util.ResponseStatusExceptionFilter; +import com.github.dockerjava.core.util.SelectiveLoggingFilter; +//import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; +// see https://github.com/docker-java/docker-java/issues/196 +import com.github.dockerjava.jaxrs.connector.ApacheConnectorProvider; + +public class DockerCmdExecFactoryImpl implements DockerCmdExecFactory { + + private static final Logger LOGGER = LoggerFactory + .getLogger(DockerCmdExecFactoryImpl.class.getName()); + private Client client; + private WebTarget baseResource; + + public void init(DockerClientConfig dockerClientConfig) { + checkNotNull(dockerClientConfig, "config was not specified"); + + ClientConfig clientConfig = new ClientConfig(); + // clientConfig.connectorProvider(new ApacheConnectorProvider()); + // see #226 + // StatsCmd could create a live stream for one container. + // Unfortunately, ApacheConnector would perform a ChunkedInputStream call that results in the application blocking. + // The reason is org.apache.http.impl.io.ChunkedInputStream would NEVER closes the underlying stream, even when close + // gets called. Instead, it will read until the "end" of its chunking on close. + // see com.github.dockerjava.api.command.StatsCmdTest + clientConfig.connectorProvider(new HttpUrlConnectorProvider()); + clientConfig.property(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, + true); + + clientConfig.register(ResponseStatusExceptionFilter.class); + clientConfig.register(JsonClientFilter.class); + clientConfig.register(JacksonJsonProvider.class); + + if (dockerClientConfig.followRedirectsFilterEnabled()) { + clientConfig.register(FollowRedirectsFilter.class); + } + + if (dockerClientConfig.isLoggingFilterEnabled()) { + clientConfig.register(new SelectiveLoggingFilter(LOGGER, true)); + } + + if (dockerClientConfig.getReadTimeout() != null) { + int readTimeout = dockerClientConfig.getReadTimeout(); + clientConfig.property(ClientProperties.READ_TIMEOUT, readTimeout); + } + + URI originalUri = dockerClientConfig.getUri(); + + SSLContext sslContext = null; + + if (dockerClientConfig.getSslConfig() != null) { + try { + sslContext = dockerClientConfig.getSslConfig().getSSLContext(); + } catch (Exception ex) { + throw new DockerClientException("Error in SSL Configuration", + ex); + } + } + + PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager( + getSchemeRegistry(originalUri, sslContext)); + + if (dockerClientConfig.getMaxTotalConnections() != null) + connManager + .setMaxTotal(dockerClientConfig.getMaxTotalConnections()); + if (dockerClientConfig.getMaxPerRoutConnections() != null) + connManager.setDefaultMaxPerRoute(dockerClientConfig + .getMaxPerRoutConnections()); + + clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, + connManager); + + ClientBuilder clientBuilder = ClientBuilder.newBuilder().withConfig( + clientConfig); + + if (sslContext != null) { + clientBuilder.sslContext(sslContext); + } + + client = clientBuilder.build(); + + if (originalUri.getScheme().equals("unix")) { + dockerClientConfig.setUri(UnixConnectionSocketFactory + .sanitizeUri(originalUri)); + } + WebTarget webResource = client.target(dockerClientConfig.getUri()); + + if (dockerClientConfig.getVersion() == null + || dockerClientConfig.getVersion().isEmpty()) { + baseResource = webResource; + } else { + baseResource = webResource.path("v" + + dockerClientConfig.getVersion()); + } + } + + private org.apache.http.config.Registry getSchemeRegistry( + final URI originalUri, SSLContext sslContext) { + RegistryBuilder registryBuilder = RegistryBuilder + .create(); + registryBuilder.register("http", + PlainConnectionSocketFactory.getSocketFactory()); + if (sslContext != null) { + registryBuilder.register("https", new SSLConnectionSocketFactory( + sslContext)); + } + registryBuilder.register("unix", new UnixConnectionSocketFactory( + originalUri)); + return registryBuilder.build(); + } + + protected WebTarget getBaseResource() { + checkNotNull(baseResource, + "Factory not initialized. You probably forgot to call init()!"); + return baseResource; + } + + public AuthCmd.Exec createAuthCmdExec() { + return new AuthCmdExec(getBaseResource()); + } + + public InfoCmd.Exec createInfoCmdExec() { + return new InfoCmdExec(getBaseResource()); + } + + public PingCmd.Exec createPingCmdExec() { + return new PingCmdExec(getBaseResource()); + } + + public VersionCmd.Exec createVersionCmdExec() { + return new VersionCmdExec(getBaseResource()); + } + + public PullImageCmd.Exec createPullImageCmdExec() { + return new PullImageCmdExec(getBaseResource()); + } + + public PushImageCmd.Exec createPushImageCmdExec() { + return new PushImageCmdExec(getBaseResource()); + } + + public SaveImageCmd.Exec createSaveImageCmdExec() { + return new SaveImageCmdExec(getBaseResource()); + } + + public CreateImageCmd.Exec createCreateImageCmdExec() { + return new CreateImageCmdExec(getBaseResource()); + } + + public SearchImagesCmd.Exec createSearchImagesCmdExec() { + return new SearchImagesCmdExec(getBaseResource()); + } + + public RemoveImageCmd.Exec createRemoveImageCmdExec() { + return new RemoveImageCmdExec(getBaseResource()); + } + + public ListImagesCmd.Exec createListImagesCmdExec() { + return new ListImagesCmdExec(getBaseResource()); + } + + public InspectImageCmd.Exec createInspectImageCmdExec() { + return new InspectImageCmdExec(getBaseResource()); + } + + public ListContainersCmd.Exec createListContainersCmdExec() { + return new ListContainersCmdExec(getBaseResource()); + } + + public CreateContainerCmd.Exec createCreateContainerCmdExec() { + return new CreateContainerCmdExec(getBaseResource()); + } + + public StartContainerCmd.Exec createStartContainerCmdExec() { + return new StartContainerCmdExec(getBaseResource()); + } + + public InspectContainerCmd.Exec createInspectContainerCmdExec() { + return new InspectContainerCmdExec(getBaseResource()); + } + + public ExecCreateCmd.Exec createExecCmdExec() { + return new ExecCreateCmdExec(getBaseResource()); + } + + public RemoveContainerCmd.Exec createRemoveContainerCmdExec() { + return new RemoveContainerCmdExec(getBaseResource()); + } + + public WaitContainerCmd.Exec createWaitContainerCmdExec() { + return new WaitContainerCmdExec(getBaseResource()); + } + + public AttachContainerCmd.Exec createAttachContainerCmdExec() { + return new AttachContainerCmdExec(getBaseResource()); + } + + public ExecStartCmd.Exec createExecStartCmdExec() { + return new ExecStartCmdExec(getBaseResource()); + } + + public InspectExecCmd.Exec createInspectExecCmdExec() { + return new InspectExecCmdExec(getBaseResource()); + } + + public LogContainerCmd.Exec createLogContainerCmdExec() { + return new LogContainerCmdExec(getBaseResource()); + } + + public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec() { + return new CopyFileFromContainerCmdExec(getBaseResource()); + } + + public StopContainerCmd.Exec createStopContainerCmdExec() { + return new StopContainerCmdExec(getBaseResource()); + } + + public ContainerDiffCmd.Exec createContainerDiffCmdExec() { + return new ContainerDiffCmdExec(getBaseResource()); + } + + public KillContainerCmd.Exec createKillContainerCmdExec() { + return new KillContainerCmdExec(getBaseResource()); + } + + public RestartContainerCmd.Exec createRestartContainerCmdExec() { + return new RestartContainerCmdExec(getBaseResource()); + } + + public CommitCmd.Exec createCommitCmdExec() { + return new CommitCmdExec(getBaseResource()); + } + + public BuildImageCmd.Exec createBuildImageCmdExec() { + return new BuildImageCmdExec(getBaseResource()); + } + + public TopContainerCmd.Exec createTopContainerCmdExec() { + return new TopContainerCmdExec(getBaseResource()); + } + + public TagImageCmd.Exec createTagImageCmdExec() { + return new TagImageCmdExec(getBaseResource()); + } + + public PauseContainerCmd.Exec createPauseContainerCmdExec() { + return new PauseContainerCmdExec(getBaseResource()); + } + + public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec() { + return new UnpauseContainerCmdExec(baseResource); + } + + public EventsCmd.Exec createEventsCmdExec() { + return new EventsCmdExec(getBaseResource()); + } + + public Exec createStatsCmdExec() { + return new StatsCmdExec(getBaseResource()); + } + + public void close() throws IOException { + checkNotNull(client, + "Factory not initialized. You probably forgot to call init()!"); + client.close(); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/EventsCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/EventsCmdExec.java new file mode 100644 index 00000000..db14ab21 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/EventsCmdExec.java @@ -0,0 +1,110 @@ +package com.github.dockerjava.jaxrs; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.command.EventCallback; +import com.github.dockerjava.api.command.EventsCmd; +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.jaxrs.util.WrappedResponseInputStream; + +public class EventsCmdExec extends + AbstrDockerCmdExec implements + EventsCmd.Exec { + private static final Logger LOGGER = LoggerFactory + .getLogger(EventsCmdExec.class); + + public EventsCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected ExecutorService execute(EventsCmd command) { + ExecutorService executorService = Executors.newSingleThreadExecutor(); + + WebTarget webResource = getBaseResource().path("/events") + .queryParam("since", command.getSince()) + .queryParam("until", command.getUntil()); + + LOGGER.trace("GET: {}", webResource); + EventNotifier eventNotifier = EventNotifier.create( + command.getEventCallback(), webResource); + executorService.submit(eventNotifier); + return executorService; + } + + private static class EventNotifier implements Callable { + private static final JsonFactory JSON_FACTORY = new JsonFactory(); + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final EventCallback eventCallback; + private final WebTarget webTarget; + + private EventNotifier(EventCallback eventCallback, WebTarget webTarget) { + this.eventCallback = eventCallback; + this.webTarget = webTarget; + } + + public static EventNotifier create(EventCallback eventCallback, + WebTarget webTarget) { + checkNotNull(eventCallback, "An EventCallback must be provided"); + checkNotNull(webTarget, "An WebTarget must be provided"); + return new EventNotifier(eventCallback, webTarget); + } + + @Override + public Void call() throws Exception { + int numEvents = 0; + Response response = null; + try { + response = webTarget.request().get(Response.class); + InputStream inputStream = new WrappedResponseInputStream( + response); + JsonParser jp = JSON_FACTORY.createParser(inputStream); + // The following condition looks strange but jp.nextToken() will block until there is an + // event from the docker server or the connection is terminated. + // therefore we want to check before getting an event (to prevent a blocking operation + // and after the event to make sure that the eventCallback is still interested in getting notified. + while (eventCallback.isReceiving() && + jp.nextToken() != JsonToken.END_OBJECT && !jp.isClosed() && + eventCallback.isReceiving()) { + try { + eventCallback.onEvent(OBJECT_MAPPER.readValue(jp, + Event.class)); + } catch (Exception e) { + eventCallback.onException(e); + } + numEvents++; + } + } catch (Exception e) { + eventCallback.onException(e); + } finally { + // call onCompletion before close because of https://github.com/docker-java/docker-java/issues/196 + try { + eventCallback.onCompletion(numEvents); + } catch (Exception e) { + eventCallback.onException(e); + } + if (response != null) { + response.close(); + } + } + + return null; + } + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/ExecCreateCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/ExecCreateCmdExec.java new file mode 100644 index 00000000..02ea4d8a --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/ExecCreateCmdExec.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.jaxrs; + +import com.github.dockerjava.api.command.ExecCreateCmd; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import static javax.ws.rs.client.Entity.entity; + +public class ExecCreateCmdExec extends AbstrDockerCmdExec implements ExecCreateCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(VersionCmdExec.class); + + public ExecCreateCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected ExecCreateCmdResponse execute(ExecCreateCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/exec").resolveTemplate("id", command.getContainerId()); + + LOGGER.trace("POST: {}", webResource); + + return webResource + .request() + .accept(MediaType.APPLICATION_JSON) + .post(entity(command, MediaType.APPLICATION_JSON), ExecCreateCmdResponse.class); + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/ExecStartCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/ExecStartCmdExec.java new file mode 100644 index 00000000..cbac951d --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/ExecStartCmdExec.java @@ -0,0 +1,39 @@ +package com.github.dockerjava.jaxrs; + +import com.github.dockerjava.api.command.ExecStartCmd; +import com.github.dockerjava.jaxrs.util.WrappedResponseInputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import java.io.InputStream; + +import static javax.ws.rs.client.Entity.entity; + +public class ExecStartCmdExec extends AbstrDockerCmdExec implements ExecStartCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExecStartCmdExec.class); + + public ExecStartCmdExec(WebTarget baseResource) { + super(baseResource); + } + + + @Override + protected InputStream execute(ExecStartCmd command) { + WebTarget webResource = getBaseResource().path("/exec/{id}/start").resolveTemplate("id", command.getExecId()); + + LOGGER.trace("POST: {}", webResource); + + Response response = webResource + .request() + .accept(MediaType.APPLICATION_JSON) + .post(entity(command, MediaType.APPLICATION_JSON), Response.class); + + return new WrappedResponseInputStream(response); + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/InfoCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/InfoCmdExec.java new file mode 100644 index 00000000..c585f31c --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/InfoCmdExec.java @@ -0,0 +1,29 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.InfoCmd; +import com.github.dockerjava.api.model.Info; + +public class InfoCmdExec extends AbstrDockerCmdExec implements InfoCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(InfoCmdExec.class); + + public InfoCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Info execute(InfoCmd command) { + WebTarget webResource = getBaseResource().path("/info"); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(Info.class); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/InspectContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/InspectContainerCmdExec.java new file mode 100644 index 00000000..664a67c5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/InspectContainerCmdExec.java @@ -0,0 +1,30 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.InspectContainerCmd; +import com.github.dockerjava.api.command.InspectContainerResponse; + +public class InspectContainerCmdExec extends AbstrDockerCmdExec implements InspectContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(InspectContainerCmdExec.class); + + public InspectContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected InspectContainerResponse execute(InspectContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/json").resolveTemplate("id", command.getContainerId()); + + LOGGER.debug("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(InspectContainerResponse.class); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/InspectExecCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/InspectExecCmdExec.java new file mode 100644 index 00000000..b9ee33d6 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/InspectExecCmdExec.java @@ -0,0 +1,24 @@ +package com.github.dockerjava.jaxrs; + +import com.github.dockerjava.api.command.InspectExecCmd; +import com.github.dockerjava.api.command.InspectExecResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +public class InspectExecCmdExec extends AbstrDockerCmdExec implements InspectExecCmd.Exec { + private static final Logger LOGGER = LoggerFactory.getLogger(InspectExecCmdExec.class); + + public InspectExecCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected InspectExecResponse execute(InspectExecCmd command) { + WebTarget webResource = getBaseResource().path("/exec/{id}/json").resolveTemplate("id", command.getExecId()); + LOGGER.debug("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(InspectExecResponse.class); + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/InspectImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/InspectImageCmdExec.java new file mode 100644 index 00000000..78426609 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/InspectImageCmdExec.java @@ -0,0 +1,29 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.InspectImageCmd; +import com.github.dockerjava.api.command.InspectImageResponse; + +public class InspectImageCmdExec extends AbstrDockerCmdExec implements InspectImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(InspectImageCmdExec.class); + + public InspectImageCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected InspectImageResponse execute(InspectImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/{id}/json").resolveTemplate("id", command.getImageId()); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(InspectImageResponse.class); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/KillContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/KillContainerCmdExec.java new file mode 100644 index 00000000..6164064c --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/KillContainerCmdExec.java @@ -0,0 +1,37 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.KillContainerCmd; + +public class KillContainerCmdExec extends + AbstrDockerCmdExec implements + KillContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(KillContainerCmdExec.class); + + public KillContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Void execute(KillContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/kill") + .resolveTemplate("id", command.getContainerId()); + + if (command.getSignal() != null) { + webResource = webResource.queryParam("signal", command.getSignal()); + } + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(null).close(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/ListContainersCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/ListContainersCmdExec.java new file mode 100644 index 00000000..018db9a5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/ListContainersCmdExec.java @@ -0,0 +1,43 @@ +package com.github.dockerjava.jaxrs; + +import java.util.List; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.ListContainersCmd; +import com.github.dockerjava.api.model.Container; + +public class ListContainersCmdExec extends AbstrDockerCmdExec> implements ListContainersCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(ListContainersCmdExec.class); + + public ListContainersCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected List execute(ListContainersCmd command) { + WebTarget webResource = getBaseResource().path("/containers/json") + .queryParam("all", command.hasShowAllEnabled() ? "1" : "0") + .queryParam("since", command.getSinceId()) + .queryParam("before", command.getBeforeId()) + .queryParam("size", command.hasShowSizeEnabled() ? "1" : "0"); + + if (command.getLimit() >= 0) { + webResource = webResource.queryParam("limit", String.valueOf(command.getLimit())); + } + + LOGGER.trace("GET: {}", webResource); + List containers = webResource.request().accept(MediaType.APPLICATION_JSON).get(new GenericType>() { + }); + LOGGER.trace("Response: {}", containers); + + return containers; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/ListImagesCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/ListImagesCmdExec.java new file mode 100644 index 00000000..59e08fb6 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/ListImagesCmdExec.java @@ -0,0 +1,48 @@ +package com.github.dockerjava.jaxrs; + +import java.util.List; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.ListImagesCmd; +import com.github.dockerjava.api.model.Image; + +import static com.google.common.net.UrlEscapers.urlPathSegmentEscaper; + +public class ListImagesCmdExec extends + AbstrDockerCmdExec> implements + ListImagesCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(ListImagesCmdExec.class); + + public ListImagesCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected List execute(ListImagesCmd command) { + WebTarget webResource = getBaseResource().path("/images/json") + .queryParam("all", command.hasShowAllEnabled() ? "1" : "0"); + + if (command.getFilters() != null) + webResource = webResource.queryParam("filters", + urlPathSegmentEscaper().escape(command.getFilters())); + + LOGGER.trace("GET: {}", webResource); + + List images = webResource.request() + .accept(MediaType.APPLICATION_JSON) + .get(new GenericType>() { + }); + LOGGER.trace("Response: {}", images); + + return images; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/LogContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/LogContainerCmdExec.java new file mode 100644 index 00000000..7d031bdd --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/LogContainerCmdExec.java @@ -0,0 +1,36 @@ +package com.github.dockerjava.jaxrs; + +import java.io.InputStream; + +import javax.ws.rs.client.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.LogContainerCmd; +import com.github.dockerjava.jaxrs.util.WrappedResponseInputStream; + +public class LogContainerCmdExec extends AbstrDockerCmdExec implements LogContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(LogContainerCmdExec.class); + + public LogContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected InputStream execute(LogContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/logs") + .resolveTemplate("id", command.getContainerId()) + .queryParam("timestamps", command.hasTimestampsEnabled() ? "1" : "0") + .queryParam("stdout", command.hasStdoutEnabled() ? "1" : "0") + .queryParam("stderr", command.hasStderrEnabled() ? "1" : "0") + .queryParam("follow", command.hasFollowStreamEnabled() ? "1" : "0") + .queryParam("tail", command.getTail() < 0 ? "all" : "" + command.getTail()); + + LOGGER.trace("GET: {}", webResource); + + return new WrappedResponseInputStream(webResource.request().get()); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/PauseContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/PauseContainerCmdExec.java new file mode 100644 index 00000000..429c61ed --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/PauseContainerCmdExec.java @@ -0,0 +1,34 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.PauseContainerCmd; + +public class PauseContainerCmdExec extends + AbstrDockerCmdExec implements + PauseContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(PauseContainerCmdExec.class); + + public PauseContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Void execute(PauseContainerCmd command) { + WebTarget webResource = getBaseResource() + .path("/containers/{id}/pause").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(null).close(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/PingCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/PingCmdExec.java new file mode 100644 index 00000000..65bdc04b --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/PingCmdExec.java @@ -0,0 +1,28 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.PingCmd; + +public class PingCmdExec extends AbstrDockerCmdExec implements PingCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(PingCmdExec.class); + + public PingCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Void execute(PingCmd command) { + WebTarget webResource = getBaseResource().path("/_ping"); + + LOGGER.trace("GET: {}", webResource); + webResource.request().get().close(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/PullImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/PullImageCmdExec.java new file mode 100644 index 00000000..bd89d22f --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/PullImageCmdExec.java @@ -0,0 +1,52 @@ +package com.github.dockerjava.jaxrs; + +import java.io.InputStream; + +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.PullImageCmd; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.jaxrs.util.WrappedResponseInputStream; + +public class PullImageCmdExec extends + AbstrDockerCmdExec implements + PullImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(PullImageCmdExec.class); + + public PullImageCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected InputStream execute(PullImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/create") + .queryParam("tag", command.getTag()) + .queryParam("fromImage", command.getRepository()) + .queryParam("registry", command.getRegistry()); + + LOGGER.trace("POST: {}", webResource); + Response response = resourceWithOptionalAuthConfig(command, webResource.request()) + .accept(MediaType.APPLICATION_OCTET_STREAM_TYPE).post(null); + + return new WrappedResponseInputStream(response); + } + + private Invocation.Builder resourceWithOptionalAuthConfig( + PullImageCmd command, Invocation.Builder request) { + AuthConfig authConfig = command.getAuthConfig(); + if (authConfig != null) { + request = request.header("X-Registry-Auth", + registryAuth(authConfig)); + } + return request; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/PushImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/PushImageCmdExec.java new file mode 100644 index 00000000..7fc88d91 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/PushImageCmdExec.java @@ -0,0 +1,89 @@ +package com.github.dockerjava.jaxrs; + + + +import static javax.ws.rs.client.Entity.entity; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.github.dockerjava.api.command.PushImageCmd; +import com.github.dockerjava.api.command.PushImageCmd.Response; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.PushEventStreamItem; + +import com.github.dockerjava.jaxrs.util.WrappedResponseInputStream; +// Shaded, but imported +import com.google.common.collect.ImmutableList; + +public class PushImageCmdExec extends AbstrDockerCmdExec implements PushImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(PushImageCmdExec.class); + + public PushImageCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected ResponseImpl execute(PushImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/" + name(command) + "/push") + .queryParam("tag", command.getTag()); + + final String registryAuth = registryAuth(command.getAuthConfig()); + LOGGER.trace("POST: {}", webResource); + javax.ws.rs.core.Response response = webResource + .request() + .header("X-Registry-Auth", registryAuth) + .accept(MediaType.APPLICATION_JSON) + .post( + entity(Response.class, MediaType.APPLICATION_JSON)); + + return new ResponseImpl(new WrappedResponseInputStream(response)); + } + + private String name(PushImageCmd command) { + String name = command.getName(); + AuthConfig authConfig = command.getAuthConfig(); + return name.contains("/") ? name : authConfig.getUsername(); + } + + + public static class ResponseImpl extends Response { + + private final InputStream proxy; + + ResponseImpl(InputStream proxy) { + this.proxy = proxy; + } + + @Override + public Iterable getItems() throws IOException { + ObjectMapper mapper = new ObjectMapper(); + // we'll be reading instances of MyBean + ObjectReader reader = mapper.reader(PushEventStreamItem.class); + // and then do other configuration, if any, and read: + Iterator items = reader.readValues(proxy); + + try { + return ImmutableList.copyOf(items); + } finally { + proxy.close(); + } + } + + @Override + public int read() throws IOException { + return proxy.read(); + } + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/RemoveContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/RemoveContainerCmdExec.java new file mode 100644 index 00000000..d2ff1b83 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/RemoveContainerCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.RemoveContainerCmd; + +public class RemoveContainerCmdExec extends AbstrDockerCmdExec implements RemoveContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(RemoveContainerCmdExec.class); + + public RemoveContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Void execute(RemoveContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/" + command.getContainerId()) + .queryParam("v", command.hasRemoveVolumesEnabled() ? "1" : "0") + .queryParam("force", command.hasForceEnabled() ? "1" : "0"); + + LOGGER.trace("DELETE: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).delete().close(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/RemoveImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/RemoveImageCmdExec.java new file mode 100644 index 00000000..d5d7b800 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/RemoveImageCmdExec.java @@ -0,0 +1,30 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.RemoveImageCmd; + +public class RemoveImageCmdExec extends AbstrDockerCmdExec implements RemoveImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(RemoveImageCmdExec.class); + + public RemoveImageCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Void execute(RemoveImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/" + command.getImageId()) + .queryParam("force", command.hasForceEnabled() ? "1" : "0") + .queryParam("noprune", command.hasNoPruneEnabled() ? "1" : "0"); + + LOGGER.trace("DELETE: {}", webResource); + webResource.request().delete().close(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/RestartContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/RestartContainerCmdExec.java new file mode 100644 index 00000000..9ac76a36 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/RestartContainerCmdExec.java @@ -0,0 +1,35 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.RestartContainerCmd; + +public class RestartContainerCmdExec extends + AbstrDockerCmdExec implements + RestartContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(RestartContainerCmdExec.class); + + public RestartContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Void execute(RestartContainerCmd command) { + WebTarget webResource = getBaseResource() + .path("/containers/{id}/restart") + .resolveTemplate("id", command.getContainerId()) + .queryParam("t", String.valueOf(command.getTimeout())); + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(null).close(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/SaveImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/SaveImageCmdExec.java new file mode 100644 index 00000000..8d70e2f6 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/SaveImageCmdExec.java @@ -0,0 +1,36 @@ +package com.github.dockerjava.jaxrs; + +import java.io.InputStream; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.SaveImageCmd; +import com.github.dockerjava.jaxrs.util.WrappedResponseInputStream; + +public class SaveImageCmdExec extends AbstrDockerCmdExec implements SaveImageCmd.Exec { + private static final Logger LOGGER = LoggerFactory + .getLogger(SaveImageCmdExec.class); + + public SaveImageCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected InputStream execute(SaveImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/" + command.getName() + "/get") + .queryParam("tag", command.getTag()); + + LOGGER.trace("GET: {}", webResource); + Response response = webResource + .request() + .accept(MediaType.APPLICATION_JSON) + .get(); + + return new WrappedResponseInputStream(response); + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/SearchImagesCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/SearchImagesCmdExec.java new file mode 100644 index 00000000..a7b6ddbc --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/SearchImagesCmdExec.java @@ -0,0 +1,32 @@ +package com.github.dockerjava.jaxrs; + +import java.util.List; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.SearchImagesCmd; +import com.github.dockerjava.api.model.SearchItem; + +public class SearchImagesCmdExec extends AbstrDockerCmdExec> implements SearchImagesCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(SearchImagesCmdExec.class); + + public SearchImagesCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected List execute(SearchImagesCmd command) { + WebTarget webResource = getBaseResource().path("/images/search").queryParam("term", command.getTerm()); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(new GenericType>() { + }); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/StartContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/StartContainerCmdExec.java new file mode 100644 index 00000000..8ca1a42f --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/StartContainerCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.jaxrs; + +import static javax.ws.rs.client.Entity.entity; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.StartContainerCmd; + +public class StartContainerCmdExec extends AbstrDockerCmdExec implements StartContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(StartContainerCmdExec.class); + + public StartContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Void execute(StartContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/start").resolveTemplate("id", command.getContainerId()); + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(entity(command, MediaType.APPLICATION_JSON)).close(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/StatsCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/StatsCmdExec.java new file mode 100644 index 00000000..64bcaced --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/StatsCmdExec.java @@ -0,0 +1,45 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.JerseyInvocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.StatsCmd; +import com.github.dockerjava.api.model.Stats; + +/** + * @author Heng WU(wuheng09@otcaix.iscas.ac.cn) + * + */ +public class StatsCmdExec extends AbstrDockerCmdExec implements + StatsCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(StatsCmdExec.class); + + public StatsCmdExec(WebTarget baseResource) { + super(baseResource); + } + + protected Stats execute(StatsCmd command) { + WebTarget webResource = getBaseResource() + .path("/containers/{id}/stats").resolveTemplate("id", + command.getContainerId()); + + LOGGER.trace("GET: {}", webResource); + JerseyInvocation.Builder request = (JerseyInvocation.Builder) webResource.request(); + // see #221 + // This call would results in the application blocking if container is shutdown. + // So we need timeout settings + // see com.github.dockerjava.api.command.StatsCmdTest + request.property(ClientProperties.READ_TIMEOUT, 5000); + return request.accept(MediaType.APPLICATION_JSON) + .get(Stats.class); + } + + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/StopContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/StopContainerCmdExec.java new file mode 100644 index 00000000..bf683be5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/StopContainerCmdExec.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.StopContainerCmd; + +public class StopContainerCmdExec extends + AbstrDockerCmdExec implements + StopContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(StopContainerCmdExec.class); + + public StopContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Void execute(StopContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/stop") + .resolveTemplate("id", command.getContainerId()) + .queryParam("t", String.valueOf(command.getTimeout())); + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON).post(null).close(); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/TagImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/TagImageCmdExec.java new file mode 100644 index 00000000..dfbeba07 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/TagImageCmdExec.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.TagImageCmd; + +public class TagImageCmdExec extends AbstrDockerCmdExec implements TagImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(TagImageCmdExec.class); + + public TagImageCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Void execute(TagImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/" + command.getImageId() + "/tag") + .queryParam("repo", command.getRepository()) + .queryParam("tag", command.getTag()) + .queryParam("force", command.hasForceEnabled() ? "1" : "0"); + + LOGGER.trace("POST: {}", webResource); + webResource.request().post(null).close(); + return null; + } + + + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/TopContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/TopContainerCmdExec.java new file mode 100644 index 00000000..55c431cc --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/TopContainerCmdExec.java @@ -0,0 +1,34 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.apache.commons.lang.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.TopContainerCmd; +import com.github.dockerjava.api.command.TopContainerResponse; + +public class TopContainerCmdExec extends AbstrDockerCmdExec implements TopContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(TopContainerCmdExec.class); + + public TopContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected TopContainerResponse execute(TopContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/top") + .resolveTemplate("id", command.getContainerId()); + + if(!StringUtils.isEmpty(command.getPsArgs())) + webResource = webResource.queryParam("ps_args", command.getPsArgs()); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON).get(TopContainerResponse.class); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/UnixConnectionSocketFactory.java b/src/main/java/com/github/dockerjava/jaxrs/UnixConnectionSocketFactory.java new file mode 100644 index 00000000..4b6cfbba --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/UnixConnectionSocketFactory.java @@ -0,0 +1,85 @@ +package com.github.dockerjava.jaxrs; +/* + * Copyright (c) 2014 Spotify AB. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + +import org.apache.http.HttpHost; +import org.apache.http.annotation.Immutable; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.protocol.HttpContext; +import org.newsclub.net.unix.AFUNIXSocketAddress; + +import java.io.File; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.net.URI; + +/** + * Provides a ConnectionSocketFactory for connecting Apache HTTP clients to Unix sockets. + */ +@Immutable +public class UnixConnectionSocketFactory implements ConnectionSocketFactory { + + private File socketFile; + + public UnixConnectionSocketFactory(final URI socketUri) { + super(); + + final String filename = socketUri.toString() + .replaceAll("^unix:///", "unix://localhost/") + .replaceAll("^unix://localhost", ""); + + this.socketFile = new File(filename); + } + + public static URI sanitizeUri(final URI uri) { + if (uri.getScheme().equals("unix")) { + return URI.create("unix://localhost:80"); + } else { + return uri; + } + } + + @Override + public Socket createSocket(final HttpContext context) throws IOException { + return new ApacheUnixSocket(); + } + + @Override + public Socket connectSocket(final int connectTimeout, + final Socket socket, + final HttpHost host, + final InetSocketAddress remoteAddress, + final InetSocketAddress localAddress, + final HttpContext context) throws IOException { + try { + socket.connect(new AFUNIXSocketAddress(socketFile), connectTimeout); + } catch (SocketTimeoutException e) { + throw new ConnectTimeoutException(e, null, remoteAddress.getAddress()); + } + + return socket; + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/UnpauseContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/UnpauseContainerCmdExec.java new file mode 100644 index 00000000..7fed603a --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/UnpauseContainerCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.UnpauseContainerCmd; + +public class UnpauseContainerCmdExec extends AbstrDockerCmdExec implements UnpauseContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(UnpauseContainerCmdExec.class); + + public UnpauseContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Void execute(UnpauseContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/unpause") + .resolveTemplate("id", command.getContainerId()); + + LOGGER.trace("POST: {}", webResource); + webResource.request().accept(MediaType.APPLICATION_JSON) + .post(null).close(); + + return null; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/VersionCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/VersionCmdExec.java new file mode 100644 index 00000000..9b4ac817 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/VersionCmdExec.java @@ -0,0 +1,30 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.VersionCmd; +import com.github.dockerjava.api.model.Version; + +public class VersionCmdExec extends AbstrDockerCmdExec implements VersionCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(VersionCmdExec.class); + + public VersionCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Version execute(VersionCmd command) { + WebTarget webResource = getBaseResource().path("/version"); + + LOGGER.trace("GET: {}", webResource); + return webResource.request().accept(MediaType.APPLICATION_JSON) + .get(Version.class); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/WaitContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/WaitContainerCmdExec.java new file mode 100644 index 00000000..1b81eee3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/WaitContainerCmdExec.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.jaxrs; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.dockerjava.api.command.WaitContainerCmd; + +public class WaitContainerCmdExec extends AbstrDockerCmdExec implements WaitContainerCmd.Exec { + + private static final Logger LOGGER = LoggerFactory + .getLogger(WaitContainerCmdExec.class); + + public WaitContainerCmdExec(WebTarget baseResource) { + super(baseResource); + } + + @Override + protected Integer execute(WaitContainerCmd command) { + WebTarget webResource = getBaseResource().path("/containers/{id}/wait") + .resolveTemplate("id", command.getContainerId()); + + LOGGER.trace("POST: {}", webResource); + ObjectNode ObjectNode = webResource.request().accept(MediaType.APPLICATION_JSON) + .post(null, ObjectNode.class); + + return ObjectNode.get("StatusCode").asInt(); + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnector.java b/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnector.java new file mode 100644 index 00000000..651db983 --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnector.java @@ -0,0 +1,698 @@ +package com.github.dockerjava.jaxrs.connector; + +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; + +import org.glassfish.jersey.SslConfigurator; +import org.glassfish.jersey.apache.connector.ApacheClientProperties; +import org.glassfish.jersey.apache.connector.LocalizationMessages; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.ClientRequest; +import org.glassfish.jersey.client.ClientResponse; +import org.glassfish.jersey.client.RequestEntityProcessing; +import org.glassfish.jersey.client.spi.AsyncConnectorCallback; +import org.glassfish.jersey.client.spi.Connector; +import org.glassfish.jersey.internal.util.PropertiesHelper; +import org.glassfish.jersey.message.internal.HeaderUtils; +import org.glassfish.jersey.message.internal.OutboundMessageContext; +import org.glassfish.jersey.message.internal.ReaderWriter; +import org.glassfish.jersey.message.internal.Statuses; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.AuthCache; +import org.apache.http.client.CookieStore; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.config.ConnectionConfig; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.ManagedHttpClientConnection; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.LayeredConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContexts; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.entity.AbstractHttpEntity; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.entity.ContentLengthStrategy; +import org.apache.http.impl.auth.BasicScheme; +import org.apache.http.impl.client.BasicAuthCache; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.DefaultManagedHttpClientConnection; +import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.impl.io.ChunkedOutputStream; +import org.apache.http.io.SessionOutputBuffer; +import org.apache.http.util.TextUtils; +import org.apache.http.util.VersionInfo; + +import jersey.repackaged.com.google.common.util.concurrent.MoreExecutors; + +/** + * A {@link Connector} that utilizes the Apache HTTP Client to send and receive + * HTTP request and responses. + *

+ * The following properties are only supported at construction of this class: + *

    + *
  • {@link ApacheClientProperties#CONNECTION_MANAGER}
  • + *
  • {@link ApacheClientProperties#REQUEST_CONFIG}
  • + *
  • {@link ApacheClientProperties#CREDENTIALS_PROVIDER}
  • + *
  • {@link ApacheClientProperties#DISABLE_COOKIES}
  • + *
  • {@link ClientProperties#PROXY_URI}
  • + *
  • {@link ClientProperties#PROXY_USERNAME}
  • + *
  • {@link ClientProperties#PROXY_PASSWORD}
  • + *
  • {@link ClientProperties#REQUEST_ENTITY_PROCESSING} - default value is {@link RequestEntityProcessing#CHUNKED}
  • + *
  • {@link ApacheClientProperties#PREEMPTIVE_BASIC_AUTHENTICATION}
  • + *
  • {@link ApacheClientProperties#SSL_CONFIG}
  • + *
+ *

+ * This connector uses {@link RequestEntityProcessing#CHUNKED chunked encoding} as a default setting. This can + * be overridden by the {@link ClientProperties#REQUEST_ENTITY_PROCESSING}. By default the + * {@link ClientProperties#CHUNKED_ENCODING_SIZE} property is only supported by using default connection manager. If custom + * connection manager needs to be used then chunked encoding size can be set by providing a custom + * {@link org.apache.http.HttpClientConnection} (via custom {@link org.apache.http.impl.conn.ManagedHttpClientConnectionFactory}) + * and overriding {@code createOutputStream} method. + *

+ *

+ * Using of authorization is dependent on the chunk encoding setting. If the entity + * buffering is enabled, the entity is buffered and authorization can be performed + * automatically in response to a 401 by sending the request again. When entity buffering + * is disabled (chunked encoding is used) then the property + * {@link org.glassfish.jersey.apache.connector.ApacheClientProperties#PREEMPTIVE_BASIC_AUTHENTICATION} must + * be set to {@code true}. + *

+ *

+ * If a {@link org.glassfish.jersey.client.ClientResponse} is obtained and an + * entity is not read from the response then + * {@link org.glassfish.jersey.client.ClientResponse#close()} MUST be called + * after processing the response to release connection-based resources. + *

+ *

+ * Client operations are thread safe, the HTTP connection may + * be shared between different threads. + *

+ *

+ * If a response entity is obtained that is an instance of {@link Closeable} + * then the instance MUST be closed after processing the entity to release + * connection-based resources. + *

+ *

+ * The following methods are currently supported: HEAD, GET, POST, PUT, DELETE, OPTIONS, PATCH and TRACE. + *

+ * + * @author jorgeluisw@mac.com + * @author Paul Sandoz (paul.sandoz at oracle.com) + * @author Pavel Bucek (pavel.bucek at oracle.com) + * @author Arul Dhesiaseelan (aruld at acm.org) + * @see ApacheClientProperties#CONNECTION_MANAGER + */ +@SuppressWarnings("deprecation") +class ApacheConnector implements Connector { + + private final static Logger LOGGER = Logger.getLogger(ApacheConnector.class.getName()); + + private static final VersionInfo vi; + private static final String release; + + static { + vi = VersionInfo.loadVersionInfo("org.apache.http.client", HttpClientBuilder.class.getClassLoader()); + release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE; + } + + private final CloseableHttpClient client; + private final CookieStore cookieStore; + private final boolean preemptiveBasicAuth; + private final RequestConfig requestConfig; + + /** + * Create the new Apache HTTP Client connector. + * + * @param config client configuration. + */ + ApacheConnector(Configuration config) { + Object reqConfig = null; + + if (config != null) { + final Object connectionManager = config.getProperties().get(ApacheClientProperties.CONNECTION_MANAGER); + + if (connectionManager != null) { + if (!(connectionManager instanceof HttpClientConnectionManager)) { + LOGGER.log( + Level.WARNING, + LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( + ApacheClientProperties.CONNECTION_MANAGER, + connectionManager.getClass().getName(), + HttpClientConnectionManager.class.getName()) + ); + } + } + + reqConfig = config.getProperties().get(ApacheClientProperties.REQUEST_CONFIG); + if (reqConfig != null) { + if (!(reqConfig instanceof RequestConfig)) { + LOGGER.log( + Level.WARNING, + LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( + ApacheClientProperties.REQUEST_CONFIG, + reqConfig.getClass().getName(), + RequestConfig.class.getName()) + ); + reqConfig = null; + } + } + } + + final SSLContext sslContext = getSslContext(config); + final HttpClientBuilder clientBuilder = HttpClientBuilder.create(); + + clientBuilder.setConnectionManager(getConnectionManager(config, sslContext)); + clientBuilder.setSslcontext(sslContext); + + final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); + + int connectTimeout = 0; + int socketTimeout = 0; + boolean ignoreCookies = false; + if (config != null) { + connectTimeout = ClientProperties.getValue(config.getProperties(), ClientProperties.CONNECT_TIMEOUT, 0); + socketTimeout = ClientProperties.getValue(config.getProperties(), ClientProperties.READ_TIMEOUT, 0); + ignoreCookies = PropertiesHelper.isProperty(config.getProperties(), ApacheClientProperties.DISABLE_COOKIES); + + final Object credentialsProvider = config.getProperty(ApacheClientProperties.CREDENTIALS_PROVIDER); + if (credentialsProvider != null && (credentialsProvider instanceof CredentialsProvider)) { + clientBuilder.setDefaultCredentialsProvider((CredentialsProvider) credentialsProvider); + } + + Object proxyUri; + proxyUri = config.getProperty(ClientProperties.PROXY_URI); + if (proxyUri != null) { + final URI u = getProxyUri(proxyUri); + final HttpHost proxy = new HttpHost(u.getHost(), u.getPort(), u.getScheme()); + String userName; + userName = ClientProperties.getValue(config.getProperties(), ClientProperties.PROXY_USERNAME, String.class); + if (userName != null) { + String password; + password = ClientProperties.getValue(config.getProperties(), ClientProperties.PROXY_PASSWORD, String.class); + + if (password != null) { + final CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials( + new AuthScope(u.getHost(), u.getPort()), + new UsernamePasswordCredentials(userName, password) + ); + clientBuilder.setDefaultCredentialsProvider(credsProvider); + } + } + clientBuilder.setProxy(proxy); + } + + final Boolean preemptiveBasicAuthProperty = (Boolean) config.getProperties() + .get(ApacheClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION); + this.preemptiveBasicAuth = (preemptiveBasicAuthProperty != null) ? preemptiveBasicAuthProperty : false; + } else { + this.preemptiveBasicAuth = false; + } + + + if (reqConfig != null) { + RequestConfig.Builder reqConfigBuilder = RequestConfig.copy((RequestConfig) reqConfig); + if (connectTimeout > 0) { + reqConfigBuilder.setConnectTimeout(connectTimeout); + } + if (socketTimeout > 0) { + reqConfigBuilder.setSocketTimeout(socketTimeout); + } + if (ignoreCookies) { + reqConfigBuilder.setCookieSpec(CookieSpecs.IGNORE_COOKIES); + } + requestConfig = reqConfigBuilder.build(); + } else { + requestConfigBuilder.setConnectTimeout(connectTimeout); + requestConfigBuilder.setSocketTimeout(socketTimeout); + if (ignoreCookies) { + requestConfigBuilder.setCookieSpec(CookieSpecs.IGNORE_COOKIES); + } + requestConfig = requestConfigBuilder.build(); + } + + if (requestConfig.getCookieSpec() == null || !requestConfig.getCookieSpec().equals(CookieSpecs.IGNORE_COOKIES)) { + this.cookieStore = new BasicCookieStore(); + clientBuilder.setDefaultCookieStore(cookieStore); + } else { + this.cookieStore = null; + } + clientBuilder.setDefaultRequestConfig(requestConfig); + this.client = clientBuilder.build(); + } + + private SSLContext getSslContext(final Configuration config) { + final SslConfigurator sslConfigurator = ApacheClientProperties.getValue( + config.getProperties(), + ApacheClientProperties.SSL_CONFIG, + SslConfigurator.class); + + return sslConfigurator != null ? sslConfigurator.createSSLContext() : null; + } + + HttpClientConnectionManager getConnectionManager(final Configuration config, final SSLContext sslContext) { + final Object cmObject = config.getProperties().get(ApacheClientProperties.CONNECTION_MANAGER); + + // Connection manager from configuration. + if (cmObject != null) { + if (cmObject instanceof HttpClientConnectionManager) { + return (HttpClientConnectionManager) cmObject; + } else { + LOGGER.log( + Level.WARNING, + LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( + ApacheClientProperties.CONNECTION_MANAGER, + cmObject.getClass().getName(), + HttpClientConnectionManager.class.getName()) + ); + } + } + + // Create custom connection manager. + return createConnectionManager( + config, + sslContext, + null, + false); + } + + private HttpClientConnectionManager createConnectionManager( + final Configuration config, + final SSLContext sslContext, + X509HostnameVerifier hostnameVerifier, + final boolean useSystemProperties) { + + final String[] supportedProtocols = useSystemProperties ? split( + System.getProperty("https.protocols")) : null; + final String[] supportedCipherSuites = useSystemProperties ? split( + System.getProperty("https.cipherSuites")) : null; + + if (hostnameVerifier == null) { + hostnameVerifier = SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; + } + + final LayeredConnectionSocketFactory sslSocketFactory; + if (sslContext != null) { + sslSocketFactory = new SSLConnectionSocketFactory( + sslContext, supportedProtocols, supportedCipherSuites, hostnameVerifier); + } else { + if (useSystemProperties) { + sslSocketFactory = new SSLConnectionSocketFactory( + (SSLSocketFactory) SSLSocketFactory.getDefault(), + supportedProtocols, supportedCipherSuites, hostnameVerifier); + } else { + sslSocketFactory = new SSLConnectionSocketFactory( + SSLContexts.createDefault(), + hostnameVerifier); + } + } + + final Registry registry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", sslSocketFactory) + .build(); + + final Integer chunkSize = ClientProperties.getValue(config.getProperties(), + ClientProperties.CHUNKED_ENCODING_SIZE, 4096, Integer.class); + + final PoolingHttpClientConnectionManager connectionManager = + new PoolingHttpClientConnectionManager(registry, new ConnectionFactory(chunkSize)); + + if (useSystemProperties) { + String s = System.getProperty("http.keepAlive", "true"); + if ("true".equalsIgnoreCase(s)) { + s = System.getProperty("http.maxConnections", "5"); + final int max = Integer.parseInt(s); + connectionManager.setDefaultMaxPerRoute(max); + connectionManager.setMaxTotal(2 * max); + } + } + + return connectionManager; + } + + private static String[] split(final String s) { + if (TextUtils.isBlank(s)) { + return null; + } + return s.split(" *, *"); + } + + /** + * Get the {@link HttpClient}. + * + * @return the {@link HttpClient}. + */ + @SuppressWarnings("UnusedDeclaration") + public HttpClient getHttpClient() { + return client; + } + + /** + * Get the {@link CookieStore}. + * + * @return the {@link CookieStore} instance or {@code null} when {@value ApacheClientProperties#DISABLE_COOKIES} set to + * {@code true}. + */ + public CookieStore getCookieStore() { + return cookieStore; + } + + private static URI getProxyUri(final Object proxy) { + if (proxy instanceof URI) { + return (URI) proxy; + } else if (proxy instanceof String) { + return URI.create((String) proxy); + } else { + throw new ProcessingException(LocalizationMessages.WRONG_PROXY_URI_TYPE(ClientProperties.PROXY_URI)); + } + } + + @Override + public ClientResponse apply(final ClientRequest clientRequest) throws ProcessingException { + final HttpUriRequest request = getUriHttpRequest(clientRequest); + final Map clientHeadersSnapshot = writeOutBoundHeaders(clientRequest.getHeaders(), request); + + try { + final CloseableHttpResponse response; + final HttpClientContext context = HttpClientContext.create(); + if (preemptiveBasicAuth) { + final AuthCache authCache = new BasicAuthCache(); + final BasicScheme basicScheme = new BasicScheme(); + authCache.put(getHost(request), basicScheme); + context.setAuthCache(authCache); + } + response = client.execute(getHost(request), request, context); + HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, clientRequest.getHeaders(), + this.getClass().getName()); + + final Response.StatusType status = response.getStatusLine().getReasonPhrase() == null ? + Statuses.from(response.getStatusLine().getStatusCode()) : + Statuses.from(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); + + final ClientResponse responseContext = new ApacheConnectorClientResponse(status, clientRequest, response); + final List redirectLocations = context.getRedirectLocations(); + if (redirectLocations != null && !redirectLocations.isEmpty()) { + responseContext.setResolvedRequestUri(redirectLocations.get(redirectLocations.size() - 1)); + } + + final Header[] respHeaders = response.getAllHeaders(); + final MultivaluedMap headers = responseContext.getHeaders(); + for (final Header header : respHeaders) { + final String headerName = header.getName(); + List list = headers.get(headerName); + if (list == null) { + list = new ArrayList(); + } + list.add(header.getValue()); + headers.put(headerName, list); + } + + final HttpEntity entity = response.getEntity(); + + if (entity != null) { + if (headers.get(HttpHeaders.CONTENT_LENGTH) == null) { + headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(entity.getContentLength())); + } + + final Header contentEncoding = entity.getContentEncoding(); + if (headers.get(HttpHeaders.CONTENT_ENCODING) == null && contentEncoding != null) { + headers.add(HttpHeaders.CONTENT_ENCODING, contentEncoding.getValue()); + } + } + + + try { + responseContext.setEntityStream(new HttpClientResponseInputStream(response)); + } catch (final IOException e) { + LOGGER.log(Level.SEVERE, null, e); + } + + return responseContext; + } catch (final Exception e) { + throw new ProcessingException(e); + } + } + + @Override + public Future apply(final ClientRequest request, final AsyncConnectorCallback callback) { + return MoreExecutors.sameThreadExecutor().submit(new Runnable() { + @Override + public void run() { + try { + callback.response(apply(request)); + } catch (final ProcessingException ex) { + callback.failure(ex); + } catch (final Throwable t) { + callback.failure(t); + } + } + }); + } + + @Override + public String getName() { + return "Apache HttpClient " + release; + } + + @Override + public void close() { + try { + client.close(); + } catch (final IOException e) { + throw new ProcessingException(LocalizationMessages.FAILED_TO_STOP_CLIENT(), e); + } + } + + private HttpHost getHost(final HttpUriRequest request) { + return new HttpHost(request.getURI().getHost(), request.getURI().getPort(), request.getURI().getScheme()); + } + + private HttpUriRequest getUriHttpRequest(final ClientRequest clientRequest) { + final Boolean redirectsEnabled = + clientRequest.resolveProperty(ClientProperties.FOLLOW_REDIRECTS, requestConfig.isRedirectsEnabled()); + final RequestConfig config = RequestConfig.copy(requestConfig).setRedirectsEnabled(redirectsEnabled).build(); + + final Boolean bufferingEnabled = clientRequest.resolveProperty(ClientProperties.REQUEST_ENTITY_PROCESSING, + RequestEntityProcessing.class) == RequestEntityProcessing.BUFFERED; + final HttpEntity entity = getHttpEntity(clientRequest, bufferingEnabled); + + return RequestBuilder + .create(clientRequest.getMethod()) + .setUri(clientRequest.getUri()) + .setConfig(config) + .setEntity(entity) + .build(); + } + + + private HttpEntity getHttpEntity(final ClientRequest clientRequest, final boolean bufferingEnabled) { + final Object entity = clientRequest.getEntity(); + + if (entity == null) { + return null; + } + + final AbstractHttpEntity httpEntity = new AbstractHttpEntity() { + @Override + public boolean isRepeatable() { + return false; + } + + @Override + public long getContentLength() { + return -1; + } + + @Override + public InputStream getContent() throws IOException, IllegalStateException { + if (bufferingEnabled) { + final ByteArrayOutputStream buffer = new ByteArrayOutputStream(512); + writeTo(buffer); + return new ByteArrayInputStream(buffer.toByteArray()); + } else { + return null; + } + } + + @Override + public void writeTo(final OutputStream outputStream) throws IOException { + clientRequest.setStreamProvider(new OutboundMessageContext.StreamProvider() { + @Override + public OutputStream getOutputStream(final int contentLength) throws IOException { + return outputStream; + } + }); + clientRequest.writeEntity(); + } + + @Override + public boolean isStreaming() { + return false; + } + }; + + if (bufferingEnabled) { + try { + return new BufferedHttpEntity(httpEntity); + } catch (final IOException e) { + throw new ProcessingException(LocalizationMessages.ERROR_BUFFERING_ENTITY(), e); + } + } else { + return httpEntity; + } + } + + private static Map writeOutBoundHeaders(final MultivaluedMap headers, final HttpUriRequest request) { + Map stringHeaders = HeaderUtils.asStringHeadersSingleValue(headers); + + for (Map.Entry e : stringHeaders.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + return stringHeaders; + } + + private static final class HttpClientResponseInputStream extends FilterInputStream { + + HttpClientResponseInputStream(final CloseableHttpResponse response) throws IOException { + super(getInputStream(response)); + } + + @Override + public void close() throws IOException { + super.close(); + } + } + + private static InputStream getInputStream(final CloseableHttpResponse response) throws IOException { + + if (response.getEntity() == null) { + return new ByteArrayInputStream(new byte[0]); + } else { + final InputStream i = response.getEntity().getContent(); + if (i.markSupported()) { + return i; + } + return new BufferedInputStream(i, ReaderWriter.BUFFER_SIZE); + } + } + + private static class ConnectionFactory extends ManagedHttpClientConnectionFactory { + + private static final AtomicLong COUNTER = new AtomicLong(); + + private final int chunkSize; + + private ConnectionFactory(final int chunkSize) { + this.chunkSize = chunkSize; + } + + @Override + public ManagedHttpClientConnection create(final HttpRoute route, final ConnectionConfig config) { + final String id = "http-outgoing-" + Long.toString(COUNTER.getAndIncrement()); + + return new HttpClientConnection(id, config.getBufferSize(), chunkSize); + } + } + + private static class HttpClientConnection extends DefaultManagedHttpClientConnection { + + private final int chunkSize; + + private HttpClientConnection(final String id, final int buffersize, final int chunkSize) { + super(id, buffersize); + + this.chunkSize = chunkSize; + } + + @Override + protected OutputStream createOutputStream(final long len, final SessionOutputBuffer outbuffer) { + if (len == ContentLengthStrategy.CHUNKED) { + return new ChunkedOutputStream(chunkSize, outbuffer); + } + return super.createOutputStream(len, outbuffer); + } + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorClientResponse.java b/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorClientResponse.java new file mode 100644 index 00000000..7ba9d8ab --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorClientResponse.java @@ -0,0 +1,52 @@ +package com.github.dockerjava.jaxrs.connector; + +import java.io.IOException; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.StatusType; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.glassfish.jersey.client.ClientRequest; +import org.glassfish.jersey.client.ClientResponse; + +/** + * Fix for https://github.com/docker-java/docker-java/issues/196 + * + * https://java.net/jira/browse/JERSEY-2852 + * + * @author marcus + * + */ +public class ApacheConnectorClientResponse extends ClientResponse { + + private CloseableHttpResponse closeableHttpResponse; + + public ApacheConnectorClientResponse(ClientRequest requestContext, + Response response) { + super(requestContext, response); + } + + public ApacheConnectorClientResponse(StatusType status, + ClientRequest requestContext, CloseableHttpResponse closeableHttpResponse) { + super(status, requestContext); + this.closeableHttpResponse = closeableHttpResponse; + } + + public ApacheConnectorClientResponse(StatusType status, + ClientRequest requestContext) { + super(status, requestContext); + } + + @Override + public void close() { + try { + closeableHttpResponse.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + super.close(); + } + + + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorProvider.java b/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorProvider.java new file mode 100644 index 00000000..fbde136f --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorProvider.java @@ -0,0 +1,151 @@ +package com.github.dockerjava.jaxrs.connector; + +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +import javax.ws.rs.client.Client; +import javax.ws.rs.core.Configurable; +import javax.ws.rs.core.Configuration; + +import org.glassfish.jersey.apache.connector.LocalizationMessages; +import org.glassfish.jersey.client.Initializable; +import org.glassfish.jersey.client.spi.Connector; +import org.glassfish.jersey.client.spi.ConnectorProvider; +import org.apache.http.client.HttpClient; + +/** + * Connector provider for Jersey {@link Connector connectors} that utilize + * Apache HTTP Client to send and receive HTTP request and responses. + *

+ * The following connector configuration properties are supported: + *

    + *
  • {@link ApacheClientProperties#CONNECTION_MANAGER}
  • + *
  • {@link ApacheClientProperties#REQUEST_CONFIG}
  • + *
  • {@link ApacheClientProperties#CREDENTIALS_PROVIDER}
  • + *
  • {@link ApacheClientProperties#DISABLE_COOKIES}
  • + *
  • {@link org.glassfish.jersey.client.ClientProperties#PROXY_URI}
  • + *
  • {@link org.glassfish.jersey.client.ClientProperties#PROXY_USERNAME}
  • + *
  • {@link org.glassfish.jersey.client.ClientProperties#PROXY_PASSWORD}
  • + *
  • {@link org.glassfish.jersey.client.ClientProperties#REQUEST_ENTITY_PROCESSING} + * - default value is {@link org.glassfish.jersey.client.RequestEntityProcessing#CHUNKED}
  • + *
  • {@link ApacheClientProperties#PREEMPTIVE_BASIC_AUTHENTICATION}
  • + *
  • {@link ApacheClientProperties#SSL_CONFIG}
  • + *
+ *

+ *

+ * Connector instances created via this connector provider use + * {@link org.glassfish.jersey.client.RequestEntityProcessing#CHUNKED chunked encoding} as a default setting. + * This can be overridden by the {@link org.glassfish.jersey.client.ClientProperties#REQUEST_ENTITY_PROCESSING}. + * By default the {@link org.glassfish.jersey.client.ClientProperties#CHUNKED_ENCODING_SIZE} property is only supported + * when using the default {@code org.apache.http.conn.HttpClientConnectionManager} instance. If custom + * connection manager is used, then chunked encoding size can be set by providing a custom + * {@code org.apache.http.HttpClientConnection} (via custom {@code org.apache.http.impl.conn.ManagedHttpClientConnectionFactory}) + * and overriding it's {@code createOutputStream} method. + *

+ *

+ * Use of authorization by the AHC-based connectors is dependent on the chunk encoding setting. + * If the entity buffering is enabled, the entity is buffered and authorization can be performed + * automatically in response to a 401 by sending the request again. When entity buffering + * is disabled (chunked encoding is used) then the property + * {@link org.glassfish.jersey.apache.connector.ApacheClientProperties#PREEMPTIVE_BASIC_AUTHENTICATION} must + * be set to {@code true}. + *

+ *

+ * If a {@link org.glassfish.jersey.client.ClientResponse} is obtained and an entity is not read from the response then + * {@link org.glassfish.jersey.client.ClientResponse#close()} MUST be called after processing the response to release + * connection-based resources. + *

+ *

+ * If a response entity is obtained that is an instance of {@link java.io.Closeable} + * then the instance MUST be closed after processing the entity to release + * connection-based resources. + *

+ *

+ * The following methods are currently supported: HEAD, GET, POST, PUT, DELETE, OPTIONS, PATCH and TRACE. + *

+ * + * @author Pavel Bucek (pavel.bucek at oracle.com) + * @author Arul Dhesiaseelan (aruld at acm.org) + * @author jorgeluisw at mac.com + * @author Marek Potociar (marek.potociar at oracle.com) + * @author Paul Sandoz (paul.sandoz at oracle.com) + * @since 2.5 + */ +public class ApacheConnectorProvider implements ConnectorProvider { + + @Override + public Connector getConnector(Client client, Configuration runtimeConfig) { + return new ApacheConnector(runtimeConfig); + } + + /** + * Retrieve the underlying Apache {@link HttpClient} instance from + * {@link org.glassfish.jersey.client.JerseyClient} or {@link org.glassfish.jersey.client.JerseyWebTarget} + * configured to use {@code ApacheConnectorProvider}. + * + * @param component {@code JerseyClient} or {@code JerseyWebTarget} instance that is configured to use + * {@code ApacheConnectorProvider}. + * @return underlying Apache {@code HttpClient} instance. + * + * @throws java.lang.IllegalArgumentException in case the {@code component} is neither {@code JerseyClient} + * nor {@code JerseyWebTarget} instance or in case the component + * is not configured to use a {@code ApacheConnectorProvider}. + * @since 2.8 + */ + public static HttpClient getHttpClient(Configurable component) { + if (!(component instanceof Initializable)) { + throw new IllegalArgumentException( + LocalizationMessages.INVALID_CONFIGURABLE_COMPONENT_TYPE(component.getClass().getName())); + } + + final Initializable initializable = (Initializable) component; + Connector connector = initializable.getConfiguration().getConnector(); + if (connector == null) { + initializable.preInitialize(); + connector = initializable.getConfiguration().getConnector(); + } + + if (connector instanceof ApacheConnector) { + return ((ApacheConnector) connector).getHttpClient(); + } + + throw new IllegalArgumentException(LocalizationMessages.EXPECTED_CONNECTOR_PROVIDER_NOT_USED()); + } +} + diff --git a/src/main/java/com/github/dockerjava/jaxrs/connector/README.txt b/src/main/java/com/github/dockerjava/jaxrs/connector/README.txt new file mode 100644 index 00000000..c3c1415f --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/connector/README.txt @@ -0,0 +1,3 @@ +This package exists as a workaround to https://java.net/jira/browse/JERSEY-2852. +It introduces ApacheConnectorClientResponse which extends ClientResponse and closes +the underlying CloseableHttpResponse when close() is called. \ No newline at end of file diff --git a/src/main/java/com/github/dockerjava/jaxrs/util/WrappedResponseInputStream.java b/src/main/java/com/github/dockerjava/jaxrs/util/WrappedResponseInputStream.java new file mode 100644 index 00000000..bf40dbfc --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/util/WrappedResponseInputStream.java @@ -0,0 +1,70 @@ +package com.github.dockerjava.jaxrs.util; + +import java.io.IOException; +import java.io.InputStream; + +import javax.ws.rs.core.Response; + +/** + * This is a wrapper around {@link Response} that acts as a {@link InputStream}. + * When this {@link WrappedResponseInputStream} is closed it closes the + * underlying {@link Response} object also to prevent connection leaks. + * + * @author marcus + */ +public class WrappedResponseInputStream extends InputStream { + + private Response response; + private InputStream delegate; + + public WrappedResponseInputStream(Response response) { + this.response = response; + this.delegate = response.readEntity(InputStream.class); + } + + public int read() throws IOException { + return delegate.read(); + } + + public int hashCode() { + return delegate.hashCode(); + } + + public int read(byte[] b) throws IOException { + return delegate.read(b); + } + + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + public int read(byte[] b, int off, int len) throws IOException { + return delegate.read(b, off, len); + } + + public long skip(long n) throws IOException { + return delegate.skip(n); + } + + public int available() throws IOException { + return delegate.available(); + } + + public void close() throws IOException { + response.close(); + delegate.close(); + } + + public void mark(int readlimit) { + delegate.mark(readlimit); + } + + public void reset() throws IOException { + delegate.reset(); + } + + public boolean markSupported() { + return delegate.markSupported(); + } + +} diff --git a/src/main/java/com/google/common/base/Preconditions.java b/src/main/java/com/google/common/base/Preconditions.java deleted file mode 100644 index 6a15fb4d..00000000 --- a/src/main/java/com/google/common/base/Preconditions.java +++ /dev/null @@ -1,443 +0,0 @@ -/* - * Copyright (C) 2007 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.base; - - -import java.util.NoSuchElementException; - -/** - * Simple static methods to be called at the start of your own methods to verify - * correct arguments and state. This allows constructs such as - *

- *     if (count <= 0) {
- *       throw new IllegalArgumentException("must be positive: " + count);
- *     }
- * - * to be replaced with the more compact - *
- *     checkArgument(count > 0, "must be positive: %s", count);
- * - * Note that the sense of the expression is inverted; with {@code Preconditions} - * you declare what you expect to be true, just as you do with an - * - * {@code assert} or a JUnit {@code assertTrue} call. - * - *

Warning: only the {@code "%s"} specifier is recognized as a - * placeholder in these messages, not the full range of {@link - * String#format(String, Object[])} specifiers. - * - *

Take care not to confuse precondition checking with other similar types - * of checks! Precondition exceptions -- including those provided here, but also - * {@link IndexOutOfBoundsException}, {@link NoSuchElementException}, {@link - * UnsupportedOperationException} and others -- are used to signal that the - * calling method has made an error. This tells the caller that it should - * not have invoked the method when it did, with the arguments it did, or - * perhaps ever. Postcondition or other invariant failures should not throw - * these types of exceptions. - * - *

See the Guava User Guide on - * using {@code Preconditions}. - * - * @author Kevin Bourrillion - * @since 2.0 (imported from Google Collections Library) - */ -public final class Preconditions { - private Preconditions() {} - - /** - * Ensures the truth of an expression involving one or more parameters to the - * calling method. - * - * @param expression a boolean expression - * @throws IllegalArgumentException if {@code expression} is false - */ - public static void checkArgument(boolean expression) { - if (!expression) { - throw new IllegalArgumentException(); - } - } - - /** - * Ensures the truth of an expression involving one or more parameters to the - * calling method. - * - * @param expression a boolean expression - * @param errorMessage the exception message to use if the check fails; will - * be converted to a string using {@link String#valueOf(Object)} - * @throws IllegalArgumentException if {@code expression} is false - */ - public static void checkArgument( - boolean expression, Object errorMessage) { - if (!expression) { - throw new IllegalArgumentException(String.valueOf(errorMessage)); - } - } - - /** - * Ensures the truth of an expression involving one or more parameters to the - * calling method. - * - * @param expression a boolean expression - * @param errorMessageTemplate a template for the exception message should the - * check fail. The message is formed by replacing each {@code %s} - * placeholder in the template with an argument. These are matched by - * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. - * Unmatched arguments will be appended to the formatted message in square - * braces. Unmatched placeholders will be left as-is. - * @param errorMessageArgs the arguments to be substituted into the message - * template. Arguments are converted to strings using - * {@link String#valueOf(Object)}. - * @throws IllegalArgumentException if {@code expression} is false - * @throws NullPointerException if the check fails and either {@code - * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let - * this happen) - */ - public static void checkArgument(boolean expression, - String errorMessageTemplate, - Object... errorMessageArgs) { - if (!expression) { - throw new IllegalArgumentException( - format(errorMessageTemplate, errorMessageArgs)); - } - } - - /** - * Ensures the truth of an expression involving the state of the calling - * instance, but not involving any parameters to the calling method. - * - * @param expression a boolean expression - * @throws IllegalStateException if {@code expression} is false - */ - public static void checkState(boolean expression) { - if (!expression) { - throw new IllegalStateException(); - } - } - - /** - * Ensures the truth of an expression involving the state of the calling - * instance, but not involving any parameters to the calling method. - * - * @param expression a boolean expression - * @param errorMessage the exception message to use if the check fails; will - * be converted to a string using {@link String#valueOf(Object)} - * @throws IllegalStateException if {@code expression} is false - */ - public static void checkState( - boolean expression, Object errorMessage) { - if (!expression) { - throw new IllegalStateException(String.valueOf(errorMessage)); - } - } - - /** - * Ensures the truth of an expression involving the state of the calling - * instance, but not involving any parameters to the calling method. - * - * @param expression a boolean expression - * @param errorMessageTemplate a template for the exception message should the - * check fail. The message is formed by replacing each {@code %s} - * placeholder in the template with an argument. These are matched by - * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. - * Unmatched arguments will be appended to the formatted message in square - * braces. Unmatched placeholders will be left as-is. - * @param errorMessageArgs the arguments to be substituted into the message - * template. Arguments are converted to strings using - * {@link String#valueOf(Object)}. - * @throws IllegalStateException if {@code expression} is false - * @throws NullPointerException if the check fails and either {@code - * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let - * this happen) - */ - public static void checkState(boolean expression, - String errorMessageTemplate, - Object... errorMessageArgs) { - if (!expression) { - throw new IllegalStateException( - format(errorMessageTemplate, errorMessageArgs)); - } - } - - /** - * Ensures that an object reference passed as a parameter to the calling - * method is not null. - * - * @param reference an object reference - * @return the non-null reference that was validated - * @throws NullPointerException if {@code reference} is null - */ - public static T checkNotNull(T reference) { - if (reference == null) { - throw new NullPointerException(); - } - return reference; - } - - /** - * Ensures that an object reference passed as a parameter to the calling - * method is not null. - * - * @param reference an object reference - * @param errorMessage the exception message to use if the check fails; will - * be converted to a string using {@link String#valueOf(Object)} - * @return the non-null reference that was validated - * @throws NullPointerException if {@code reference} is null - */ - public static T checkNotNull(T reference, Object errorMessage) { - if (reference == null) { - throw new NullPointerException(String.valueOf(errorMessage)); - } - return reference; - } - - /** - * Ensures that an object reference passed as a parameter to the calling - * method is not null. - * - * @param reference an object reference - * @param errorMessageTemplate a template for the exception message should the - * check fail. The message is formed by replacing each {@code %s} - * placeholder in the template with an argument. These are matched by - * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. - * Unmatched arguments will be appended to the formatted message in square - * braces. Unmatched placeholders will be left as-is. - * @param errorMessageArgs the arguments to be substituted into the message - * template. Arguments are converted to strings using - * {@link String#valueOf(Object)}. - * @return the non-null reference that was validated - * @throws NullPointerException if {@code reference} is null - */ - public static T checkNotNull(T reference, - String errorMessageTemplate, - Object... errorMessageArgs) { - if (reference == null) { - // If either of these parameters is null, the right thing happens anyway - throw new NullPointerException( - format(errorMessageTemplate, errorMessageArgs)); - } - return reference; - } - - /* - * All recent hotspots (as of 2009) *really* like to have the natural code - * - * if (guardExpression) { - * throw new BadException(messageExpression); - * } - * - * refactored so that messageExpression is moved to a separate - * String-returning method. - * - * if (guardExpression) { - * throw new BadException(badMsg(...)); - * } - * - * The alternative natural refactorings into void or Exception-returning - * methods are much slower. This is a big deal - we're talking factors of - * 2-8 in microbenchmarks, not just 10-20%. (This is a hotspot optimizer - * bug, which should be fixed, but that's a separate, big project). - * - * The coding pattern above is heavily used in java.util, e.g. in ArrayList. - * There is a RangeCheckMicroBenchmark in the JDK that was used to test this. - * - * But the methods in this class want to throw different exceptions, - * depending on the args, so it appears that this pattern is not directly - * applicable. But we can use the ridiculous, devious trick of throwing an - * exception in the middle of the construction of another exception. - * Hotspot is fine with that. - */ - - /** - * Ensures that {@code index} specifies a valid element in an array, - * list or string of size {@code size}. An element index may range from zero, - * inclusive, to {@code size}, exclusive. - * - * @param index a user-supplied index identifying an element of an array, list - * or string - * @param size the size of that array, list or string - * @return the value of {@code index} - * @throws IndexOutOfBoundsException if {@code index} is negative or is not - * less than {@code size} - * @throws IllegalArgumentException if {@code size} is negative - */ - public static int checkElementIndex(int index, int size) { - return checkElementIndex(index, size, "index"); - } - - /** - * Ensures that {@code index} specifies a valid element in an array, - * list or string of size {@code size}. An element index may range from zero, - * inclusive, to {@code size}, exclusive. - * - * @param index a user-supplied index identifying an element of an array, list - * or string - * @param size the size of that array, list or string - * @param desc the text to use to describe this index in an error message - * @return the value of {@code index} - * @throws IndexOutOfBoundsException if {@code index} is negative or is not - * less than {@code size} - * @throws IllegalArgumentException if {@code size} is negative - */ - public static int checkElementIndex( - int index, int size, String desc) { - // Carefully optimized for execution by hotspot (explanatory comment above) - if (index < 0 || index >= size) { - throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); - } - return index; - } - - private static String badElementIndex(int index, int size, String desc) { - if (index < 0) { - return format("%s (%s) must not be negative", desc, index); - } else if (size < 0) { - throw new IllegalArgumentException("negative size: " + size); - } else { // index >= size - return format("%s (%s) must be less than size (%s)", desc, index, size); - } - } - - /** - * Ensures that {@code index} specifies a valid position in an array, - * list or string of size {@code size}. A position index may range from zero - * to {@code size}, inclusive. - * - * @param index a user-supplied index identifying a position in an array, list - * or string - * @param size the size of that array, list or string - * @return the value of {@code index} - * @throws IndexOutOfBoundsException if {@code index} is negative or is - * greater than {@code size} - * @throws IllegalArgumentException if {@code size} is negative - */ - public static int checkPositionIndex(int index, int size) { - return checkPositionIndex(index, size, "index"); - } - - /** - * Ensures that {@code index} specifies a valid position in an array, - * list or string of size {@code size}. A position index may range from zero - * to {@code size}, inclusive. - * - * @param index a user-supplied index identifying a position in an array, list - * or string - * @param size the size of that array, list or string - * @param desc the text to use to describe this index in an error message - * @return the value of {@code index} - * @throws IndexOutOfBoundsException if {@code index} is negative or is - * greater than {@code size} - * @throws IllegalArgumentException if {@code size} is negative - */ - public static int checkPositionIndex( - int index, int size, String desc) { - // Carefully optimized for execution by hotspot (explanatory comment above) - if (index < 0 || index > size) { - throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); - } - return index; - } - - private static String badPositionIndex(int index, int size, String desc) { - if (index < 0) { - return format("%s (%s) must not be negative", desc, index); - } else if (size < 0) { - throw new IllegalArgumentException("negative size: " + size); - } else { // index > size - return format("%s (%s) must not be greater than size (%s)", - desc, index, size); - } - } - - /** - * Ensures that {@code start} and {@code end} specify a valid positions - * in an array, list or string of size {@code size}, and are in order. A - * position index may range from zero to {@code size}, inclusive. - * - * @param start a user-supplied index identifying a starting position in an - * array, list or string - * @param end a user-supplied index identifying a ending position in an array, - * list or string - * @param size the size of that array, list or string - * @throws IndexOutOfBoundsException if either index is negative or is - * greater than {@code size}, or if {@code end} is less than {@code start} - * @throws IllegalArgumentException if {@code size} is negative - */ - public static void checkPositionIndexes(int start, int end, int size) { - // Carefully optimized for execution by hotspot (explanatory comment above) - if (start < 0 || end < start || end > size) { - throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); - } - } - - private static String badPositionIndexes(int start, int end, int size) { - if (start < 0 || start > size) { - return badPositionIndex(start, size, "start index"); - } - if (end < 0 || end > size) { - return badPositionIndex(end, size, "end index"); - } - // end < start - return format("end index (%s) must not be less than start index (%s)", - end, start); - } - - /** - * Substitutes each {@code %s} in {@code template} with an argument. These - * are matched by position - the first {@code %s} gets {@code args[0]}, etc. - * If there are more arguments than placeholders, the unmatched arguments will - * be appended to the end of the formatted message in square braces. - * - * @param template a non-null string containing 0 or more {@code %s} - * placeholders. - * @param args the arguments to be substituted into the message - * template. Arguments are converted to strings using - * {@link String#valueOf(Object)}. Arguments can be null. - */ - static String format(String template, Object... args) { - template = String.valueOf(template); // null -> "null" - - // start substituting the arguments into the '%s' placeholders - StringBuilder builder = new StringBuilder( - template.length() + 16 * args.length); - int templateStart = 0; - int i = 0; - while (i < args.length) { - int placeholderStart = template.indexOf("%s", templateStart); - if (placeholderStart == -1) { - break; - } - builder.append(template.substring(templateStart, placeholderStart)); - builder.append(args[i++]); - templateStart = placeholderStart + 2; - } - builder.append(template.substring(templateStart)); - - // if we run out of placeholders, append the extra args in square braces - if (i < args.length) { - builder.append(" ["); - builder.append(args[i++]); - while (i < args.length) { - builder.append(", "); - builder.append(args[i++]); - } - builder.append(']'); - } - - return builder.toString(); - } -} diff --git a/src/main/java/com/kpelykh/docker/client/Config.java b/src/main/java/com/kpelykh/docker/client/Config.java deleted file mode 100644 index f6e59d76..00000000 --- a/src/main/java/com/kpelykh/docker/client/Config.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.kpelykh.docker.client; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.net.URI; -import java.util.Properties; - -class Config { - URI url; - String version, username, password, email; - - private Config() { - } - - static Config createConfig() throws DockerException { - final Properties p = new Properties(); - - try { - p.load(Config.class.getResourceAsStream("/docker.io.properties")); - } catch (IOException e) { - throw new DockerException(e); - } - - final File file = new File(System.getProperty("user.home"), ".docker.io.properties"); - - if (file.isFile()) { - try { - final FileInputStream in = new FileInputStream(file); - try { - p.load(in); - } finally { - in.close(); - } - } catch (IOException e) { - throw new DockerException(e); - } - } - - for (String s : new String[]{"url", "version", "username", "password", "email"}) { - final String key = "docker.io." + s; - if (System.getProperties().keySet().contains(key)) { - p.setProperty(key, System.getProperty(key)); - } - } - - final Config c = new Config(); - - c.url = URI.create(p.getProperty("docker.io.url")); - c.version = p.getProperty("docker.io.version"); - c.username = p.getProperty("docker.io.username"); - c.password = p.getProperty("docker.io.password"); - c.email = p.getProperty("docker.io.email"); - - return c; - } -} diff --git a/src/main/java/com/kpelykh/docker/client/DockerClient.java b/src/main/java/com/kpelykh/docker/client/DockerClient.java deleted file mode 100644 index 101d7209..00000000 --- a/src/main/java/com/kpelykh/docker/client/DockerClient.java +++ /dev/null @@ -1,954 +0,0 @@ -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 (kpelykh@gmail.com) - */ -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 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 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 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 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>() { - }); - } 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 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 getImages() throws DockerException { - return this.getImages(null, false); - } - - public List getImages(boolean allContainers) throws DockerException { - return this.getImages(null, allContainers); - } - - public List getImages(String name) throws DockerException { - return this.getImages(name, false); - } - - public List getImages(String name, boolean allImages) throws DockerException { - - MultivaluedMap 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 images = webResource.accept(MediaType.APPLICATION_JSON).get(new GenericType>() { - }); - 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 listContainers(boolean allContainers) { - return this.listContainers(allContainers, false, -1, false, null, null); - } - - public List listContainers(boolean allContainers, boolean latest) { - return this.listContainers(allContainers, latest, -1, false, null, null); - } - - public List listContainers(boolean allContainers, boolean latest, int limit) { - return this.listContainers(allContainers, latest, limit, false, null, null); - } - - public List listContainers(boolean allContainers, boolean latest, int limit, boolean showSize) { - return this.listContainers(allContainers, latest, limit, showSize, null, null); - } - - public List listContainers(boolean allContainers, boolean latest, int limit, boolean showSize, String since) { - return this.listContainers(allContainers, latest, limit, false, since, null); - } - - public List listContainers(boolean allContainers, boolean latest, int limit, boolean showSize, String since, String before) { - - MultivaluedMap 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 containers = webResource.accept(MediaType.APPLICATION_JSON).get(new GenericType>() { - }); - 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 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 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 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 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>() { - }); - } 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 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 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 dockerFileContent = FileUtils.readLines(dockerFile); - - if (dockerFileContent.size() <= 0) { - throw new DockerException(String.format("Dockerfile %s is empty", dockerFile)); - } - - List filesToAdd = new ArrayList(); - 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); - } - } -} diff --git a/src/main/java/com/kpelykh/docker/client/DockerException.java b/src/main/java/com/kpelykh/docker/client/DockerException.java deleted file mode 100644 index aa3df660..00000000 --- a/src/main/java/com/kpelykh/docker/client/DockerException.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.kpelykh.docker.client; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ - -public class DockerException extends Exception { - - public DockerException() { - } - - public DockerException(String message) { - super(message); - } - - public DockerException(String message, Throwable cause) { - super(message, cause); - } - - public DockerException(Throwable cause) { - super(cause); - } -} diff --git a/src/main/java/com/kpelykh/docker/client/NotFoundException.java b/src/main/java/com/kpelykh/docker/client/NotFoundException.java deleted file mode 100644 index 76569f7b..00000000 --- a/src/main/java/com/kpelykh/docker/client/NotFoundException.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.kpelykh.docker.client; - -/** - * Indicates that the given entity does not exist. - * - * @author Ryan Campbell ryan.campbell@gmail.com - */ -public class NotFoundException extends DockerException { - - public NotFoundException(String message) { - super(message); - } - -} diff --git a/src/main/java/com/kpelykh/docker/client/model/AuthConfig.java b/src/main/java/com/kpelykh/docker/client/model/AuthConfig.java deleted file mode 100644 index 514e79be..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/AuthConfig.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class AuthConfig { - @JsonProperty - private String username; - @JsonProperty - private String password; - @JsonProperty - private String email; - @JsonProperty("serveraddress") - private String serverAddress = "https://index.docker.io/v1/"; - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getServerAddress() { - return serverAddress; - } - - public void setServerAddress(String serverAddress) { - this.serverAddress = serverAddress; - } - - @Override - public String toString() { - return "AuthConfig{" + - "username='" + username + '\'' + - ", password='" + password + '\'' + - ", email='" + email + '\'' + - ", serverAddress='" + serverAddress + '\'' + - '}'; - } -} diff --git a/src/main/java/com/kpelykh/docker/client/model/BoundHostVolumes.java b/src/main/java/com/kpelykh/docker/client/model/BoundHostVolumes.java deleted file mode 100644 index 9a912439..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/BoundHostVolumes.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * @author Kevin A. Archie - * - */ -@JsonSerialize(using=BoundHostVolumes.Serializer.class) -public class BoundHostVolumes { - private static final String[] STRING_ARRAY = new String[0]; - private final String[] dests, binds; - - /** - * - * @param specs Iterable of String binding specs, each of form "{host-path}:{container-patch}:[rw|ro]" - * @throws MalformedVolumeSpecException if any specs are null or empty - */ - public BoundHostVolumes(final Iterable specs) { - final List dests = new ArrayList(), binds = new ArrayList(); - for (final String spec : specs) { - if (null == spec || "".equals(spec)) { - // skip empty spec lines - } else { - final String[] sspec = spec.split(":"); - dests.add(sspec.length > 1 ? sspec[1] : sspec[0]); - binds.add(spec); - } - } - this.dests = dests.toArray(STRING_ARRAY); - this.binds = binds.toArray(STRING_ARRAY); - } - - public String[] asBinds() { - return binds; - } - - private BoundHostVolumes writeVolumes(final JsonGenerator jg) throws IOException { - jg.writeStartObject(); - for (final String dest : dests) { - jg.writeObjectFieldStart(dest); - jg.writeEndObject(); - } - jg.writeEndObject(); - return this; - } - - /** - * This is an ugly hack. We assume that the serializer only gets called when - * a containing ContainerConfig gets serialized, when POSTing to - * /containers/create . In that context, we pass only the container-path - * part (the key in the volumes map). - * - * @author Kevin A. Archie - * - */ - public static class Serializer extends JsonSerializer { - /* (non-Javadoc) - * @see org.codehaus.jackson.map.JsonSerializer#serialize(java.lang.Object, org.codehaus.jackson.JsonGenerator, org.codehaus.jackson.map.SerializerProvider) - */ - @Override - public void serialize(final BoundHostVolumes volumes, final JsonGenerator jg, final SerializerProvider sp) - throws IOException { - volumes.writeVolumes(jg); - } - } -} diff --git a/src/main/java/com/kpelykh/docker/client/model/CommitConfig.java b/src/main/java/com/kpelykh/docker/client/model/CommitConfig.java deleted file mode 100644 index 4023deb7..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/CommitConfig.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class CommitConfig { - - @JsonProperty("container") - private String container; - - @JsonProperty("repo") - private String repo; - - @JsonProperty("tag") - private String tag; - - @JsonProperty("m") - private String message; - - //author (eg. “John Hannibal Smith ”) - @JsonProperty("author") - private String author; - - //config automatically applied when the image is run. (ex: {“Cmd”: [“cat”, “/world”], “PortSpecs”:[“22”]}) - @JsonProperty("run") - private String run; - - public String getContainer() { - return container; - } - - public String getRepo() { - return repo; - } - - public String getTag() { - return tag; - } - - public String getMessage() { - return message; - } - - public String getAuthor() { - return author; - } - - public String getRun() { - return run; - } - - public CommitConfig setRepo(String repo) { - this.repo = repo; - return this; - } - - public CommitConfig setTag(String tag) { - this.tag = tag; - return this; - } - - public CommitConfig setMessage(String message) { - this.message = message; - return this; - } - - public CommitConfig setAuthor(String author) { - this.author = author; - return this; - } - - public CommitConfig setRun(String run) { - this.run = run; - return this; - } - - public CommitConfig(String container) { - this.container = container; - } - -} diff --git a/src/main/java/com/kpelykh/docker/client/model/Container.java b/src/main/java/com/kpelykh/docker/client/model/Container.java deleted file mode 100644 index 3e6554cb..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/Container.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Arrays; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown=true) -public class Container { - - @JsonProperty("Id") - private String id; - - @JsonProperty("Command") - private String command; - - @JsonProperty("Image") - private String image; - - @JsonProperty("Created") - private long created; - - @JsonProperty("Status") - private String status; - - /* Example: - "Ports": { - "22/tcp": [ - { - "HostIp": "0.0.0.0", - "HostPort": "8022" - } - ] - } - */ - - @JsonProperty("Ports") - public Ports ports; - - @JsonProperty("SizeRw") - private int size; - - @JsonProperty("SizeRootFs") - private int sizeRootFs; - - @JsonProperty("Names") - private String[] names; - - public String getId() { - return id; - } - - public String getCommand() { - return command; - } - - public String getImage() { - return image; - } - - public long getCreated() { - return created; - } - - public String getStatus() { - return status; - } - - public Ports getPorts() { - return ports; - } - - public void setPorts(Ports ports) { - this.ports = ports; - } - - public int getSize() { - return size; - } - - public int getSizeRootFs() { - return sizeRootFs; - } - - public String[] getNames() { - return names; - } - - public void setId(String id) { - this.id = id; - } - - public void setCommand(String command) { - this.command = command; - } - - public void setImage(String image) { - this.image = image; - } - - public void setCreated(long created) { - this.created = created; - } - - public void setStatus(String status) { - this.status = status; - } - - public void setSize(int size) { - this.size = size; - } - - public void setSizeRootFs(int sizeRootFs) { - this.sizeRootFs = sizeRootFs; - } - - public void setNames(String[] names) { - this.names = names; - } - - @Override - public String toString() { - return "Container{" + - "id='" + id + '\'' + - ", command='" + command + '\'' + - ", image='" + image + '\'' + - ", created=" + created + - ", status='" + status + '\'' + - ", ports=" + ports + - ", size=" + size + - ", sizeRootFs=" + sizeRootFs + - ", names=" + Arrays.toString(names) + - '}'; - } -} diff --git a/src/main/java/com/kpelykh/docker/client/model/ContainerConfig.java b/src/main/java/com/kpelykh/docker/client/model/ContainerConfig.java deleted file mode 100644 index 27a1d7e3..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/ContainerConfig.java +++ /dev/null @@ -1,291 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Arrays; -import java.util.Map; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ContainerConfig { - - @JsonProperty("Hostname") private String hostName = ""; - @JsonProperty("PortSpecs") private String[] portSpecs; - @JsonProperty("User") private String user = ""; - @JsonProperty("Tty") private boolean tty = false; - @JsonProperty("OpenStdin") private boolean stdinOpen = false; - @JsonProperty("StdinOnce") private boolean stdInOnce = false; - @JsonProperty("Memory") private long memoryLimit = 0; - @JsonProperty("MemorySwap") private long memorySwap = 0; - @JsonProperty("CpuShares") private int cpuShares = 0; - @JsonProperty("AttachStdin") private boolean attachStdin = false; - @JsonProperty("AttachStdout") private boolean attachStdout = false; - @JsonProperty("AttachStderr") private boolean attachStderr = false; - @JsonProperty("Env") private String[] env; - @JsonProperty("Cmd") private String[] cmd; - @JsonProperty("Dns") private String[] dns; - @JsonProperty("Image") private String image; - @JsonProperty("Volumes") private BoundHostVolumes volumes; - @JsonProperty("VolumesFrom") private String volumesFrom = ""; - @JsonProperty("Entrypoint") private String[] entrypoint = new String[]{}; - @JsonProperty("NetworkDisabled") private boolean networkDisabled = false; - @JsonProperty("Privileged") private boolean privileged = false; - @JsonProperty("WorkingDir") private String workingDir = ""; - @JsonProperty("Domainname") private String domainName = ""; - // FIXME Is this the right type? -BJE - @JsonProperty("ExposedPorts") private Map exposedPorts; - - @JsonProperty("OnBuild") private int[] onBuild; - - public Map getExposedPorts() { - return exposedPorts; - } - - public boolean isNetworkDisabled() { - return networkDisabled; - } - - public String getDomainName() { - return domainName; - } - - public String getWorkingDir() { return workingDir; } - - public ContainerConfig setWorkingDir(String workingDir) { - this.workingDir = workingDir; - return this; - } - - public boolean isPrivileged() { - return privileged; - } - - public ContainerConfig setPrivileged(boolean privileged) { - this.privileged = privileged; - return this; - } - - public String getHostName() { - return hostName; - } - - public ContainerConfig setNetworkDisabled(boolean networkDisabled) { - this.networkDisabled = networkDisabled; - return this; - } - - public ContainerConfig setHostName(String hostName) { - this.hostName = hostName; - return this; - } - - public String[] getPortSpecs() { - return portSpecs; - } - - public ContainerConfig setPortSpecs(String[] portSpecs) { - this.portSpecs = portSpecs; - return this; - } - - public String getUser() { - return user; - } - - public ContainerConfig setUser(String user) { - this.user = user; - return this; - } - - public boolean isTty() { - return tty; - } - - public ContainerConfig setTty(boolean tty) { - this.tty = tty; - return this; - } - - public boolean isStdinOpen() { - return stdinOpen; - } - - public ContainerConfig setStdinOpen(boolean stdinOpen) { - this.stdinOpen = stdinOpen; - return this; - } - - public boolean isStdInOnce() { - return stdInOnce; - } - - public ContainerConfig setStdInOnce(boolean stdInOnce) { - this.stdInOnce = stdInOnce; - return this; - } - - public long getMemoryLimit() { - return memoryLimit; - } - - public ContainerConfig setMemoryLimit(long memoryLimit) { - this.memoryLimit = memoryLimit; - return this; - } - - public long getMemorySwap() { - return memorySwap; - } - - public ContainerConfig setMemorySwap(long memorySwap) { - this.memorySwap = memorySwap; - return this; - } - - public int getCpuShares() { - return cpuShares; - } - - public ContainerConfig setCpuShares(int cpuShares) { - this.cpuShares = cpuShares; - return this; - } - - public boolean isAttachStdin() { - return attachStdin; - } - - public ContainerConfig setAttachStdin(boolean attachStdin) { - this.attachStdin = attachStdin; - return this; - } - - public boolean isAttachStdout() { - return attachStdout; - } - - public ContainerConfig setAttachStdout(boolean attachStdout) { - this.attachStdout = attachStdout; - return this; - } - - public boolean isAttachStderr() { - return attachStderr; - } - - public ContainerConfig setAttachStderr(boolean attachStderr) { - this.attachStderr = attachStderr; - return this; - } - - public String[] getEnv() { - return env; - } - - public ContainerConfig setEnv(String[] env) { - this.env = env; - return this; - } - - public String[] getCmd() { - return cmd; - } - - public ContainerConfig setCmd(String[] cmd) { - this.cmd = cmd; - return this; - } - - public String[] getDns() { - return dns; - } - - public ContainerConfig setDns(String[] dns) { - this.dns = dns; - return this; - } - - public String getImage() { - return image; - } - - public ContainerConfig setImage(String image) { - this.image = image; - return this; - } - - public BoundHostVolumes getVolumes() { - return volumes; - } - - public ContainerConfig setVolumes(BoundHostVolumes volumes) { - this.volumes = volumes; - return this; - } - - public String getVolumesFrom() { - return volumesFrom; - } - - public ContainerConfig setVolumesFrom(String volumesFrom) { - this.volumesFrom = volumesFrom; - return this; - } - - public String[] getEntrypoint() { - return entrypoint; - } - - public ContainerConfig setEntrypoint(String[] entrypoint) { - this.entrypoint = entrypoint; - return this; - } - - public void setOnBuild(int[] onBuild) { - this.onBuild = onBuild; - } - - public int[] getOnBuild() { - return onBuild; - } - - public void setDomainName(String domainName) { - this.domainName = domainName; - } - - - @Override - public String toString() { - return "ContainerConfig{" + - "hostName='" + hostName + '\'' + - ", portSpecs=" + Arrays.toString(portSpecs) + - ", user='" + user + '\'' + - ", tty=" + tty + - ", stdinOpen=" + stdinOpen + - ", stdInOnce=" + stdInOnce + - ", memoryLimit=" + memoryLimit + - ", memorySwap=" + memorySwap + - ", cpuShares=" + cpuShares + - ", attachStdin=" + attachStdin + - ", attachStdout=" + attachStdout + - ", attachStderr=" + attachStderr + - ", env=" + Arrays.toString(env) + - ", cmd=" + Arrays.toString(cmd) + - ", dns=" + Arrays.toString(dns) + - ", image='" + image + '\'' + - ", volumes=" + volumes + - ", volumesFrom='" + volumesFrom + '\'' + - ", entrypoint=" + Arrays.toString(entrypoint) + - ", networkDisabled=" + networkDisabled + - ", privileged=" + privileged + - ", workingDir='" + workingDir + '\'' + - ", domainName='" + domainName + '\'' + - ", onBuild='" + Arrays.toString(onBuild) + '\'' + - '}'; - } -} diff --git a/src/main/java/com/kpelykh/docker/client/model/ContainerInspectResponse.java b/src/main/java/com/kpelykh/docker/client/model/ContainerInspectResponse.java deleted file mode 100644 index d686f9eb..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/ContainerInspectResponse.java +++ /dev/null @@ -1,270 +0,0 @@ -package com.kpelykh.docker.client.model; - - -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ContainerInspectResponse { - - @JsonProperty("ID") - private String id; - - @JsonProperty("Created") - private String created; - - @JsonProperty("Path") - private String path; - - @JsonProperty("Args") - private String[] args; - - @JsonProperty("Config") - public ContainerConfig config; - - @JsonProperty("State") - private ContainerState state; - - @JsonProperty("Image") - private String imageId; - - @JsonProperty("NetworkSettings") - private NetworkSettings networkSettings; - - @JsonProperty("SysInitPath") - private String sysInitPath; - - @JsonProperty("ResolvConfPath") - private String resolvConfPath; - - @JsonProperty("Volumes") - private Map volumes; - - @JsonProperty("VolumesRW") - private Map volumesRW; - - @JsonProperty("HostnamePath") - private String hostnamePath; - - @JsonProperty("HostsPath") - private String hostsPath; - - @JsonProperty("Name") - private String name; - - @JsonProperty("Driver") - private String driver; - - @JsonProperty("HostConfig") - private HostConfig hostConfig; - - @JsonProperty("ExecDriver") - private String execDriver; - - @JsonProperty("MountLabel") - private String mountLabel; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getCreated() { - return created; - } - - public void setCreated(String created) { - this.created = created; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public String[] getArgs() { - return args; - } - - public void setArgs(String[] args) { - this.args = args; - } - - public ContainerConfig getConfig() { - return config; - } - - public void setConfig(ContainerConfig config) { - this.config = config; - } - - public ContainerState getState() { - return state; - } - - public void setState(ContainerState state) { - this.state = state; - } - - public String getImageId() { - return imageId; - } - - public void setImageId(String image) { - this.imageId = image; - } - - public NetworkSettings getNetworkSettings() { - return networkSettings; - } - - public void setNetworkSettings(NetworkSettings networkSettings) { - this.networkSettings = networkSettings; - } - - public String getSysInitPath() { - return sysInitPath; - } - - public void setSysInitPath(String sysInitPath) { - this.sysInitPath = sysInitPath; - } - - public String getResolvConfPath() { - return resolvConfPath; - } - - public void setResolvConfPath(String resolvConfPath) { - this.resolvConfPath = resolvConfPath; - } - - public Map getVolumes() { - return volumes; - } - - public void setVolumes(Map volumes) { - this.volumes = volumes; - } - - public Map getVolumesRW() { - return volumesRW; - } - - public void setVolumesRW(Map volumesRW) { - this.volumesRW = volumesRW; - } - - public String getHostnamePath() { - return hostnamePath; - } - - public void setHostnamePath(String hostnamePath) { - this.hostnamePath = hostnamePath; - } - - public String getHostsPath() { - return hostsPath; - } - - public void setHostsPath(String hostsPath) { - this.hostsPath = hostsPath; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDriver() { - return driver; - } - - public void setDriver(String driver) { - this.driver = driver; - } - - public HostConfig getHostConfig() { - return hostConfig; - } - - public void setHostConfig(HostConfig hostConfig) { - this.hostConfig = hostConfig; - } - - public void setExecDriver(String execDriver) { - this.execDriver = execDriver; - } - - public String getExecDriver() { - return execDriver; - } - - public String getMountLabel() { - return mountLabel; - } - - public void setMountLabel(String mountLabel) { - this.mountLabel = mountLabel; - } - - public class NetworkSettings { - - @JsonProperty("IPAddress") public String ipAddress; - @JsonProperty("IPPrefixLen") public int ipPrefixLen; - @JsonProperty("Gateway") public String gateway; - @JsonProperty("Bridge") public String bridge; - @JsonProperty("PortMapping") public Map> portMapping; - @JsonProperty("Ports") public Ports ports; - - @Override - public String toString() { - return "NetworkSettings{" + - "ports=" + ports + - ", portMapping=" + portMapping + - ", bridge='" + bridge + '\'' + - ", gateway='" + gateway + '\'' + - ", ipPrefixLen=" + ipPrefixLen + - ", ipAddress='" + ipAddress + '\'' + - '}'; - } - } - - public class ContainerState { - - @JsonProperty("Running") public boolean running; - @JsonProperty("Pid") public int pid; - @JsonProperty("ExitCode") public int exitCode; - @JsonProperty("StartedAt") public String startedAt; - @JsonProperty("Ghost") public boolean ghost; - @JsonProperty("FinishedAt") private String finishedAt; - - @Override - public String toString() { - return "ContainerState{" + - "running=" + running + - ", pid=" + pid + - ", exitCode=" + exitCode + - ", startedAt='" + startedAt + '\'' + - ", ghost=" + ghost + - ", finishedAt='" + finishedAt + '\'' + - '}'; - } - } - -} diff --git a/src/main/java/com/kpelykh/docker/client/model/CopyConfig.java b/src/main/java/com/kpelykh/docker/client/model/CopyConfig.java deleted file mode 100755 index 696778a2..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/CopyConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Configuration object for copy command. - * @author Victor Lyuboslavsky - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class CopyConfig { - - @JsonProperty("HostPath") - private String hostPath; - - @JsonProperty("Resource") - private String resource; - - /** - * Constructor. - */ - public CopyConfig() { - hostPath = "."; - } - - /** - * Retrieves the 'resource' variable. - * @return the 'resource' variable value - */ - public String getResource() { - return resource; - } - - /** - * Sets the 'resource' variable. - * @param resource the new 'resource' variable value to set - */ - public void setResource(String resource) { - this.resource = resource; - } - - /** - * Retrieves the 'hostPath' variable. - * @return the 'hostPath' variable value - */ - public String getHostPath() { - return hostPath; - } - - /** - * Sets the 'hostPath' variable. - * @param hostPath the new 'hostPath' variable value to set - */ - public void setHostPath(String hostPath) { - this.hostPath = hostPath; - } - - @Override - public String toString() { - return "{\"HostPath\":\"" + hostPath + "\", \"Resource\":\"" + resource + "\"}"; - } - -} diff --git a/src/main/java/com/kpelykh/docker/client/model/HostConfig.java b/src/main/java/com/kpelykh/docker/client/model/HostConfig.java deleted file mode 100644 index dc7d4258..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/HostConfig.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Arrays; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class HostConfig { - - @JsonProperty("Binds") - private String[] binds; - - @JsonProperty("ContainerIDFile") - private String containerIDFile; - - @JsonProperty("LxcConf") - private LxcConf[] lxcConf; - - - @JsonProperty("Links") - private String[] links; - - @JsonProperty("PortBindings") - private Ports portBindings; - - @JsonProperty("Privileged") - private boolean privileged; - - @JsonProperty("PublishAllPorts") - private boolean publishAllPorts; - - @JsonProperty("Dns") - private String dns; - - @JsonProperty("DnsSearch") - private String dnsSearch; - - @JsonProperty("VolumesFrom") - private String volumesFrom; - - public HostConfig() { - this.binds = null; - } - - - public String[] getBinds() { - return binds; - } - - public void setBinds(String[] binds) { - this.binds = binds; - } - - public String getContainerIDFile() { - return containerIDFile; - } - - public void setContainerIDFile(String containerIDFile) { - this.containerIDFile = containerIDFile; - } - - public LxcConf[] getLxcConf() { - return lxcConf; - } - - public void setLxcConf(LxcConf[] lxcConf) { - this.lxcConf = lxcConf; - } - - public String[] getLinks() { - return links; - } - - public void setLinks(String[] links) { - this.links = links; - } - - public Ports getPortBindings() { - return portBindings; - } - - public void setPortBindings(Ports portBindings) { - this.portBindings = portBindings; - } - - public boolean isPrivileged() { - return privileged; - } - - public void setPrivileged(boolean privileged) { - this.privileged = privileged; - } - - public boolean isPublishAllPorts() { - return publishAllPorts; - } - - public void setPublishAllPorts(boolean publishAllPorts) { - this.publishAllPorts = publishAllPorts; - } - - public String getDns() { - return dns; - } - - public void setDns(String dns) { - this.dns = dns; - } - - public void setDnsSearch(String dnsSearch) { - this.dnsSearch = dnsSearch; - } - - public String getDnsSearch() { - return dnsSearch; - } - - public void setVolumesFrom(String volumesFrom) { - this.volumesFrom = volumesFrom; - } - - public String getVolumesFrom() { - return volumesFrom; - } - - @Override - public String toString() { - return "HostConfig{" + - "binds=" + Arrays.toString(binds) + - ", containerIDFile='" + containerIDFile + '\'' + - ", lxcConf=" + Arrays.toString(lxcConf) + - ", links=" + Arrays.toString(links) + - ", portBindings=" + portBindings + - ", privileged=" + privileged + - ", publishAllPorts=" + publishAllPorts + - ", dns='" + dns + '\'' + - '}'; - } - - public class LxcConf { - @JsonProperty("Key") - public String key; - - @JsonProperty("Value") - public String value; - - public LxcConf(String key, String value) { - this.key = key; - this.value = value; - } - - public LxcConf() { - } - - public String getKey() { - return key; - } - - public LxcConf setKey(String key) { - this.key = key; - return this; - } - - public String getValue() { - return value; - } - - public LxcConf setValue(String value) { - this.value = value; - return this; - } - - } -} diff --git a/src/main/java/com/kpelykh/docker/client/model/IBuilder.java b/src/main/java/com/kpelykh/docker/client/model/IBuilder.java deleted file mode 100644 index 7f5f1606..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/IBuilder.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.kpelykh.docker.client.model; - -/** - * Created by ben on 12/12/13. - */ -public interface IBuilder { - - T build(); -} diff --git a/src/main/java/com/kpelykh/docker/client/model/Image.java b/src/main/java/com/kpelykh/docker/client/model/Image.java deleted file mode 100644 index cfc578ce..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/Image.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Arrays; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Image { - - @JsonProperty("Id") - private String id; - - @JsonProperty("RepoTags") - private String[] repoTags; - - @JsonProperty("Repository") - private String repository; - - @JsonProperty("Tag") - private String tag; - - - @JsonProperty("ParentId") - private String parentId; - - @JsonProperty("Created") - private long created; - - @JsonProperty("Size") - private long size; - - @JsonProperty("VirtualSize") - private long virtualSize; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String[] getRepoTags() { - return repoTags; - } - - public void setRepoTags(String[] repoTags) { - this.repoTags = repoTags; - } - - public String getRepository() { - return repository; - } - - public void setRepository(String repository) { - this.repository = repository; - } - - public String getTag() { - return tag; - } - - public void setTag(String tag) { - this.tag = tag; - } - - public String getParentId() { - return parentId; - } - - public void setParentId(String parentId) { - this.parentId = parentId; - } - - public long getCreated() { - return created; - } - - public void setCreated(long created) { - this.created = created; - } - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - - public long getVirtualSize() { - return virtualSize; - } - - public void setVirtualSize(long virtualSize) { - this.virtualSize = virtualSize; - } - - @Override - public String toString() { - return "Image{" + - "virtualSize=" + virtualSize + - ", id='" + id + '\'' + - ", repoTags=" + Arrays.toString(repoTags) + - ", repository='" + repository + '\'' + - ", tag='" + tag + '\'' + - ", parentId='" + parentId + '\'' + - ", created=" + created + - ", size=" + size + - '}'; - } -} diff --git a/src/main/java/com/kpelykh/docker/client/model/ImageInspectResponse.java b/src/main/java/com/kpelykh/docker/client/model/ImageInspectResponse.java deleted file mode 100644 index 3514670c..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/ImageInspectResponse.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ImageInspectResponse { - - @JsonProperty("id") - private String id; - - @JsonProperty("parent") private String parent; - - @JsonProperty("created") private String created; - - @JsonProperty("container") private String container; - - @JsonProperty("container_config") private ContainerConfig containerConfig; - - @JsonProperty("Size") private long size; - - @JsonProperty("docker_version") private String dockerVersion; - - @JsonProperty("config") private ContainerConfig config; - - @JsonProperty("architecture") private String arch; - - @JsonProperty("comment") private String comment; - - @JsonProperty("author") private String author; - - @JsonProperty("os") private String os; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getParent() { - return parent; - } - - public void setParent(String parent) { - this.parent = parent; - } - - public String getCreated() { - return created; - } - - public void setCreated(String created) { - this.created = created; - } - - public String getContainer() { - return container; - } - - public void setContainer(String container) { - this.container = container; - } - - public ContainerConfig getContainerConfig() { - return containerConfig; - } - - public void setContainerConfig(ContainerConfig containerConfig) { - this.containerConfig = containerConfig; - } - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - - public String getDockerVersion() { - return dockerVersion; - } - - public void setDockerVersion(String dockerVersion) { - this.dockerVersion = dockerVersion; - } - - public ContainerConfig getConfig() { - return config; - } - - public void setConfig(ContainerConfig config) { - this.config = config; - } - - public String getArch() { - return arch; - } - - public void setArch(String arch) { - this.arch = arch; - } - - public String getComment() { - return comment; - } - - public void setComment(String comment) { - this.comment = comment; - } - - public String getAuthor() { - return author; - } - - public void setAuthor(String author) { - this.author = author; - } - - public String getOs() { - return os; - } - - public void setOs(String os) { - this.os = os; - } - - @Override - public String toString() { - return "ImageInspectResponse{" + - "id='" + id + '\'' + - ", parent='" + parent + '\'' + - ", created='" + created + '\'' + - ", container='" + container + '\'' + - ", containerConfig=" + containerConfig + - ", size=" + size + - ", dockerVersion='" + dockerVersion + '\'' + - ", config=" + config + - ", arch='" + arch + '\'' + - ", comment='" + comment + '\'' + - ", author='" + author + '\'' + - ", os='" + os + '\'' + - '}'; - } -} diff --git a/src/main/java/com/kpelykh/docker/client/model/Info.java b/src/main/java/com/kpelykh/docker/client/model/Info.java deleted file mode 100644 index 72f2f360..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/Info.java +++ /dev/null @@ -1,227 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -import java.util.List; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -public class Info { - - @JsonProperty("Debug") - private boolean debug; - - @JsonProperty("Containers") - private int containers; - - @JsonProperty("Driver") - private String driver; - - @JsonProperty("DriverStatus") - private List driverStatuses; - - - @JsonProperty("Images") - private int images; - - @JsonProperty("IPv4Forwarding") - private String IPv4Forwarding; - - @JsonProperty("IndexServerAddress") - private String IndexServerAddress; - - - @JsonProperty("InitPath") - private String initPath; - - @JsonProperty("InitSha1") - private String initSha1; - - @JsonProperty("KernelVersion") - private String kernelVersion; - - @JsonProperty("LXCVersion") - private String lxcVersion; - - @JsonProperty("MemoryLimit") - private boolean memoryLimit; - - @JsonProperty("NEventsListener") - private long nEventListener; - - @JsonProperty("NFd") - private int NFd; - - @JsonProperty("NGoroutines") - private int NGoroutines; - - @JsonProperty("SwapLimit") - private int swapLimit; - - @JsonProperty("ExecutionDriver") - private String executionDriver; - - public boolean isDebug() { - return debug; - } - - public void setDebug(boolean debug) { - this.debug = debug; - } - - public int getContainers() { - return containers; - } - - public void setContainers(int containers) { - this.containers = containers; - } - - public String getDriver() { - return driver; - } - - public void setDriver(String driver) { - this.driver = driver; - } - - public List getDriverStatuses() { - return driverStatuses; - } - - public void setDriverStatuses(List driverStatuses) { - this.driverStatuses = driverStatuses; - } - - public int getImages() { - return images; - } - - public void setImages(int images) { - this.images = images; - } - - public String getIPv4Forwarding() { - return IPv4Forwarding; - } - - public void setIPv4Forwarding(String IPv4Forwarding) { - this.IPv4Forwarding = IPv4Forwarding; - } - - public String getIndexServerAddress() { - return IndexServerAddress; - } - - public void setIndexServerAddress(String indexServerAddress) { - IndexServerAddress = indexServerAddress; - } - - public String getInitPath() { - return initPath; - } - - public void setInitPath(String initPath) { - this.initPath = initPath; - } - - public String getInitSha1() { - return initSha1; - } - - public void setInitSha1(String initSha1) { - this.initSha1 = initSha1; - } - - public String getKernelVersion() { - return kernelVersion; - } - - public void setKernelVersion(String kernelVersion) { - this.kernelVersion = kernelVersion; - } - - public String getLxcVersion() { - return lxcVersion; - } - - public void setLxcVersion(String lxcVersion) { - this.lxcVersion = lxcVersion; - } - - public boolean isMemoryLimit() { - return memoryLimit; - } - - public void setMemoryLimit(boolean memoryLimit) { - this.memoryLimit = memoryLimit; - } - - public long getnEventListener() { - return nEventListener; - } - - public void setnEventListener(long nEventListener) { - this.nEventListener = nEventListener; - } - - public int getNFd() { - return NFd; - } - - public void setNFd(int NFd) { - this.NFd = NFd; - } - - public int getNGoroutines() { - return NGoroutines; - } - - public void setNGoroutines(int NGoroutines) { - this.NGoroutines = NGoroutines; - } - - public int getSwapLimit() { - return swapLimit; - } - - public void setSwapLimit(int swapLimit) { - this.swapLimit = swapLimit; - } - public String getExecutionDriver() { - return executionDriver; - } - - public void setExecutionDriver(String executionDriver) { - this.executionDriver=executionDriver; - } - - @Override - public String toString() { - return "Info{" + - "debug=" + debug + - ", containers=" + containers + - ", driver='" + driver + '\'' + - ", driverStatuses=" + driverStatuses + - ", images=" + images + - ", IPv4Forwarding='" + IPv4Forwarding + '\'' + - ", IndexServerAddress='" + IndexServerAddress + '\'' + - ", initPath='" + initPath + '\'' + - ", initSha1='" + initSha1 + '\'' + - ", kernelVersion='" + kernelVersion + '\'' + - ", lxcVersion='" + lxcVersion + '\'' + - ", memoryLimit=" + memoryLimit + - ", nEventListener=" + nEventListener + - ", NFd=" + NFd + - ", NGoroutines=" + NGoroutines + - ", swapLimit=" + swapLimit + - '}'; - } -} diff --git a/src/main/java/com/kpelykh/docker/client/model/Port.java b/src/main/java/com/kpelykh/docker/client/model/Port.java deleted file mode 100644 index 538213ec..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/Port.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * @author Nicolas De Loof - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Port { - - @JsonProperty("PrivatePort") - private long privatePort; - - @JsonProperty("PublicPort") - private long publicPort; - - @JsonProperty("Type") - private String type; - - public long getPrivatePort() { - return privatePort; - } - - public void setPrivatePort(long privatePort) { - this.privatePort = privatePort; - } - - public long getPublicPort() { - return publicPort; - } - - public void setPublicPort(long publicPort) { - this.publicPort = publicPort; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - @Override - public String toString() { - return "Port{" + - "privatePort=" + privatePort + - ", publicPort=" + publicPort + - ", type='" + type + '\'' + - '}'; - } -} \ No newline at end of file diff --git a/src/main/java/com/kpelykh/docker/client/model/Ports.java b/src/main/java/com/kpelykh/docker/client/model/Ports.java deleted file mode 100644 index fc590289..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/Ports.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.kpelykh.docker.client.model; - - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.ObjectCodec; -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.NullNode; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -/** - * Created by ben on 16/12/13. - */ -@JsonDeserialize(using=Ports.Deserializer.class) -@JsonSerialize(using=Ports.Serializer.class) -public class Ports { - - - private final Map ports = new HashMap(); - - public Ports() { } - - public void addPort(Port port) { - ports.put(port.getPort(), port); - } - - @Override - public String toString(){ - return ports.toString(); - } - - public Map getAllPorts(){ - return ports; - } - - public static class Port{ - - private final String scheme; - private final String port; - private final String hostIp; - private final String hostPort; - - public Port(String scheme_, String port_, String hostIp_, String hostPort_) { - scheme = scheme_; - port = port_; - hostIp = hostIp_; - hostPort = hostPort_; - } - - public String getScheme() { - return scheme; - } - - public String getPort() { - return port; - } - - public String getHostIp() { - return hostIp; - } - - public String getHostPort() { - return hostPort; - } - - public static Port makePort(String full, String hostIp, String hostPort) { - if (full == null) return null; - String[] pieces = full.split("/"); - return new Port(pieces[1], pieces[0], hostIp, hostPort); - } - - @Override - public String toString() { - return "Port{" + - "scheme='" + scheme + '\'' + - ", port='" + port + '\'' + - ", hostIp='" + hostIp + '\'' + - ", hostPort='" + hostPort + '\'' + - '}'; - } - } - - public static class Deserializer extends JsonDeserializer { - @Override - public Ports deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { - - Ports out = new Ports(); - ObjectCodec oc = jsonParser.getCodec(); - JsonNode node = oc.readTree(jsonParser); - for (Iterator> it = node.fields(); it.hasNext();) { - - Map.Entry field = it.next(); - if (!field.getValue().equals(NullNode.getInstance())) { - String hostIp = field.getValue().get(0).get("HostIp").textValue(); - String hostPort = field.getValue().get(0).get("HostPort").textValue(); - out.addPort(Port.makePort(field.getKey(), hostIp, hostPort)); - } - } - return out; - } - } - - public static class Serializer extends JsonSerializer { - - @Override - public void serialize(Ports ports, JsonGenerator jsonGen, - SerializerProvider serProvider) throws IOException, JsonProcessingException { - - jsonGen.writeStartObject();//{ - for(String portKey : ports.getAllPorts().keySet()){ - Port p = ports.getAllPorts().get(portKey); - jsonGen.writeFieldName(p.getPort() + "/" + p.getScheme()); - jsonGen.writeStartArray(); - jsonGen.writeStartObject(); - jsonGen.writeStringField("HostIp", p.hostIp); - jsonGen.writeStringField("HostPort", p.hostPort); - jsonGen.writeEndObject(); - jsonGen.writeEndArray(); - } - jsonGen.writeEndObject();//} - } - - } - -} \ No newline at end of file diff --git a/src/main/java/com/kpelykh/docker/client/model/Version.java b/src/main/java/com/kpelykh/docker/client/model/Version.java deleted file mode 100644 index a427aafb..00000000 --- a/src/main/java/com/kpelykh/docker/client/model/Version.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.kpelykh.docker.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Version { - - @JsonProperty("Version") - private String version; - - @JsonProperty("GitCommit") - private String gitCommit; - - @JsonProperty("GoVersion") - private String goVersion; - - @JsonProperty("KernelVersion") - private String kernelVersion; - - @JsonProperty("Arch") - private String arch; - - @JsonProperty("Os") - private String operatingSystem; - - @JsonProperty("ApiVersion") - private String apiVersion; - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getGitCommit() { - return gitCommit; - } - - public void setGitCommit(String gitCommit) { - this.gitCommit = gitCommit; - } - - public String getGoVersion() { - return goVersion; - } - - public void setGoVersion(String goVersion) { - this.goVersion = goVersion; - } - - public String getKernelVersion() { - return kernelVersion; - } - - public void setKernelVersion(String kernelVersion) { - this.kernelVersion = kernelVersion; - } - - public String getArch() { - return arch; - } - - public void setArch(String arch) { - this.arch = arch; - } - - public String getOperatingSystem() { - return operatingSystem; - } - - public void setOperatingSystem(String operatingSystem) { - this.operatingSystem = operatingSystem; - } - - public void setApiVersion(String apiVersion) { - this.apiVersion = apiVersion; - } - - public String getApiVersion() { - return apiVersion; - } - - @Override - public String toString() { - return "Version{" + - "version='" + version + '\'' + - ", gitCommit='" + gitCommit + '\'' + - ", goVersion='" + goVersion + '\'' + - ", kernelVersion='" + kernelVersion + '\'' + - ", arch='" + arch + '\'' + - ", operatingSystem='" + operatingSystem + '\'' + - ", apiVersion='" + apiVersion + '\'' + - '}'; - } -} diff --git a/src/main/java/com/kpelykh/docker/client/utils/JsonClientFilter.java b/src/main/java/com/kpelykh/docker/client/utils/JsonClientFilter.java deleted file mode 100644 index 55b8b287..00000000 --- a/src/main/java/com/kpelykh/docker/client/utils/JsonClientFilter.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.kpelykh.docker.client.utils; - - -import com.sun.jersey.api.client.ClientRequest; -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.filter.ClientFilter; - -/** - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - * - */ -public class JsonClientFilter extends ClientFilter { - - public ClientResponse handle(ClientRequest cr) { - // Call the next filter - ClientResponse resp = getNext().handle(cr); - String respContentType = resp.getHeaders().getFirst("Content-Type"); - if (respContentType.startsWith("text/plain")) { - String newContentType = "application/json" + respContentType.substring(10); - resp.getHeaders().putSingle("Content-Type", newContentType); - } - return resp; - } - -} diff --git a/src/main/resources/META-INF/services/com.github.dockerjava.api.command.DockerCmdExecFactory b/src/main/resources/META-INF/services/com.github.dockerjava.api.command.DockerCmdExecFactory new file mode 100644 index 00000000..f0686bc9 --- /dev/null +++ b/src/main/resources/META-INF/services/com.github.dockerjava.api.command.DockerCmdExecFactory @@ -0,0 +1 @@ +com.github.dockerjava.jaxrs.DockerCmdExecFactoryImpl \ No newline at end of file diff --git a/src/main/resources/docker.io.properties b/src/main/resources/docker.io.properties index 0ce162bd..6f0bfc86 100644 --- a/src/main/resources/docker.io.properties +++ b/src/main/resources/docker.io.properties @@ -1,2 +1,6 @@ -docker.io.url=http://localhost:4243 -docker.io.version=1.11 \ No newline at end of file +docker.io.url=https://localhost:2376 +docker.io.enableLoggingFilter=true +docker.io.dockerCertPath=${user.home}/.docker +docker.io.dockerCfgPath=${user.home}/.dockercfg +docker.io.username=${user.name} +docker.io.serverAddress=https://index.docker.io/v1/ \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/api/command/CommandJSONSamples.java b/src/test/java/com/github/dockerjava/api/command/CommandJSONSamples.java new file mode 100644 index 00000000..500919fd --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/command/CommandJSONSamples.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015 CloudBees Inc., Oleg Nenashev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.dockerjava.api.command; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.test.serdes.AbstractJSONResourceRef; +import com.github.dockerjava.test.serdes.JSONResourceRef; +import java.io.IOException; +import java.io.InputStream; +import org.apache.commons.io.IOUtils; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + * References test resources and provides basic tests functionality. + * @author Oleg Nenashev + */ +public enum CommandJSONSamples implements JSONResourceRef { + + inspectContainerResponse_full, + inspectContainerResponse_empty; + + @Override + public String getFileName() { + return this + ".json"; + } + + @Override + public Class getResourceClass() { + return CommandJSONSamples.class; + } +} diff --git a/src/test/java/com/github/dockerjava/api/command/InspectContainerResponseTest.java b/src/test/java/com/github/dockerjava/api/command/InspectContainerResponseTest.java new file mode 100644 index 00000000..2e294b14 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/command/InspectContainerResponseTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015 CloudBees Inc., Oleg Nenashev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.dockerjava.api.command; + +import static com.github.dockerjava.test.serdes.JSONTestHelper.testRoundTrip; +import java.io.IOException; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +/** + * Tests for {@link InspectContainerResponse}. + * @author Oleg Nenashev + */ +public class InspectContainerResponseTest { + + @Test + public void roundTrip_full() throws IOException { + InspectContainerResponse[] responses = testRoundTrip( + CommandJSONSamples.inspectContainerResponse_full, + InspectContainerResponse[].class); + assertEquals(1, responses.length); + final InspectContainerResponse response = responses[0]; + + // Check volumes: https://github.com/docker-java/docker-java/issues/211 + assertEquals(response.getVolumes().length, 2); + assertEquals(response.getVolumesRW().length, 2); + assertEquals(response.getVolumes()[1].getContainerPath(), "/bar/foo/myvol2"); + assertEquals(response.getVolumes()[1].getHostPath(), "/path2"); + assertEquals(response.getVolumesRW()[1].getVolume().getPath(), "/bar/foo/myvol2"); + assertFalse(response.getVolumesRW()[1].getAccessMode().toBoolean()); + assertTrue(response.getVolumesRW()[0].getAccessMode().toBoolean()); + } + + @Test + public void roundTrip_empty() throws IOException { + testRoundTrip(CommandJSONSamples.inspectContainerResponse_empty, InspectContainerResponse[].class); + } +} diff --git a/src/test/java/com/github/dockerjava/api/command/StatsCmdTest.java b/src/test/java/com/github/dockerjava/api/command/StatsCmdTest.java new file mode 100644 index 00000000..d5caeb9a --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/command/StatsCmdTest.java @@ -0,0 +1,67 @@ +package com.github.dockerjava.api.command; + +import java.io.IOException; + +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.glassfish.jersey.client.JerseyClient; +import org.glassfish.jersey.client.JerseyClientBuilder; + +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.Stats; +import com.github.dockerjava.core.DockerClientBuilder; + +/** + * @author Heng WU(wuheng09@otcaix.iscas.ac.cn) + * + * if container is not exist, throws com.github.dockerjava.api.NotFoundException + * if container is shutdown, throws java.net.SocketTimeoutException + * if container is startup, return Object Stats + */ +public class StatsCmdTest { + + final static String DOCKER_SERVER = "http://127.0.0.1:2375"; + + final static String STARTUP_CONTAINER_ID = "4b4fb43779bcc2cb4d5aad954dac99224ff1c2ea623cd95322ef657a5c2f2c36"; + + final static String SHUTDOWN_CONTAINER_ID = "c571b02e26d30e03dfc21829de618df440275c303fbddb384679a5949d0de5d0"; + + final static String NOT_EXIST_CONTAINER_ID = "test1234"; + + /** + * @param args + * @throws IOException + */ + public static void main(String[] args) throws IOException { + noramlStatsImplWithContainer(); +// simpleStatsImplWithContainer(); + } + + + private static void noramlStatsImplWithContainer() throws IOException { + DockerClient client = DockerClientBuilder.getInstance(DOCKER_SERVER) + .build(); + Stats stats = client.statsCmd(SHUTDOWN_CONTAINER_ID).exec(); + System.out.println("Read:" + stats.getRead()); + System.out.println("Cpu_stats:" + stats.getCpu_stats()); + System.out.println("Memory_stats:" + stats.getMemory_stats()); + client.close(); + } + + private static void simpleStatsImplWithContainer() { + JerseyClient jcli = new JerseyClientBuilder().build(); + jcli.register(JacksonJsonProvider.class); + WebTarget baseTar = jcli.target(DOCKER_SERVER); + WebTarget target = baseTar.path("/containers/" + SHUTDOWN_CONTAINER_ID + + "/stats"); + Stats stats = target.request().accept(MediaType.APPLICATION_JSON) + .get(Stats.class); + System.out.println("Read:" + stats.getRead()); + System.out.println("Cpu_stats:" + stats.getCpu_stats()); + System.out.println("Memory_stats:" + stats.getMemory_stats()); + jcli.close(); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/AccessModeTest.java b/src/test/java/com/github/dockerjava/api/model/AccessModeTest.java new file mode 100644 index 00000000..432f7b00 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/AccessModeTest.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.api.model; + +import static com.github.dockerjava.api.model.AccessMode.rw; +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +public class AccessModeTest { + + @Test + public void defaultAccessMode() { + assertEquals(AccessMode.DEFAULT, rw); + } + + @Test + public void stringify() { + assertEquals(AccessMode.rw.toString(), "rw"); + } + + @Test + public void fromString() { + assertEquals(AccessMode.valueOf("rw"), rw); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "No enum const.*") + public void fromIllegalString() { + AccessMode.valueOf("xx"); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/AuthConfigTest.java b/src/test/java/com/github/dockerjava/api/model/AuthConfigTest.java new file mode 100644 index 00000000..1ed618ed --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/AuthConfigTest.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.api.model; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +public class AuthConfigTest { + + private AuthConfig authConfig; + + @BeforeMethod + public void setUp() throws Exception { + authConfig = new AuthConfig(); + authConfig.setEmail("foo"); + authConfig.setPassword("bar"); + authConfig.setServerAddress("baz"); + authConfig.setUsername("qux"); + } + + @Test + public void string() throws Exception { + assertEquals(authConfig.toString(), + "AuthConfig{username='qux', password='bar', email='foo', serverAddress='baz'}"); + } + + @Test + public void defaultServerAddress() throws Exception { + assertEquals(new AuthConfig().getServerAddress(), "https://index.docker.io/v1/"); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/api/model/BindTest.java b/src/test/java/com/github/dockerjava/api/model/BindTest.java new file mode 100644 index 00000000..50a41fc3 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/BindTest.java @@ -0,0 +1,68 @@ +package com.github.dockerjava.api.model; + +import static com.github.dockerjava.api.model.AccessMode.ro; +import static com.github.dockerjava.api.model.AccessMode.rw; +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +public class BindTest { + + @Test + public void parseUsingDefaultAccessMode() { + Bind bind = Bind.parse("/host:/container"); + assertEquals(bind.getPath(), "/host"); + assertEquals(bind.getVolume().getPath(), "/container"); + assertEquals(bind.getAccessMode(), AccessMode.DEFAULT); + } + + @Test + public void parseReadWrite() { + Bind bind = Bind.parse("/host:/container:rw"); + assertEquals(bind.getPath(), "/host"); + assertEquals(bind.getVolume().getPath(), "/container"); + assertEquals(bind.getAccessMode(), rw); + } + + @Test + public void parseReadOnly() { + Bind bind = Bind.parse("/host:/container:ro"); + assertEquals(bind.getPath(), "/host"); + assertEquals(bind.getVolume().getPath(), "/container"); + assertEquals(bind.getAccessMode(), ro); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing Bind.*") + public void parseInvalidAccessMode() { + Bind.parse("/host:/container:xx"); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing Bind 'nonsense'") + public void parseInvalidInput() { + Bind.parse("nonsense"); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing Bind 'null'") + public void parseNull() { + Bind.parse(null); + } + + @Test + public void toStringReadOnly() { + assertEquals(Bind.parse("/host:/container:ro").toString(), "/host:/container:ro"); + } + + @Test + public void toStringReadWrite() { + assertEquals(Bind.parse("/host:/container:rw").toString(), "/host:/container:rw"); + } + + @Test + public void toStringDefaultAccessMode() { + assertEquals(Bind.parse("/host:/container").toString(), "/host:/container:rw"); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/BindingTest.java b/src/test/java/com/github/dockerjava/api/model/BindingTest.java new file mode 100644 index 00000000..0379dcdb --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/BindingTest.java @@ -0,0 +1,58 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +import com.github.dockerjava.api.model.Ports.Binding; + +public class BindingTest { + + @Test + public void parseIpAndPort() { + assertEquals(Binding.parse("127.0.0.1:80"), Ports.Binding("127.0.0.1", 80)); + } + + @Test + public void parsePortOnly() { + assertEquals(Binding.parse("80"), Ports.Binding(null, 80)); + } + + @Test + public void parseIPOnly() { + assertEquals(Binding.parse("127.0.0.1"), Ports.Binding("127.0.0.1", null)); + } + + @Test + public void parseEmptyString() { + assertEquals(Binding.parse(""), Ports.Binding(null, null)); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing Binding 'nonsense'") + public void parseInvalidInput() { + Binding.parse("nonsense"); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing Binding 'null'") + public void parseNull() { + Binding.parse(null); + } + + @Test + public void toStringIpAndHost() { + assertEquals(Binding.parse("127.0.0.1:80").toString(), "127.0.0.1:80"); + } + + @Test + public void toStringPortOnly() { + assertEquals(Binding.parse("80").toString(), "80"); + } + + @Test + public void toStringIpOnly() { + assertEquals(Binding.parse("127.0.0.1").toString(), "127.0.0.1"); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/CapabilityTest.java b/src/test/java/com/github/dockerjava/api/model/CapabilityTest.java new file mode 100644 index 00000000..eb6e2a54 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/CapabilityTest.java @@ -0,0 +1,29 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class CapabilityTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void serializeCapability() throws Exception { + String json = objectMapper.writeValueAsString(Capability.ALL); + assertEquals(json, "\"ALL\""); + } + + @Test + public void deserializeCapability() throws Exception { + Capability capability = objectMapper.readValue("\"ALL\"", Capability.class); + assertEquals(capability, Capability.ALL); + } + + @Test(expectedExceptions = JsonMappingException.class) + public void deserializeInvalidCapability() throws Exception { + objectMapper.readValue("\"nonsense\"", Capability.class); + } +} diff --git a/src/test/java/com/github/dockerjava/api/model/ExposedPortTest.java b/src/test/java/com/github/dockerjava/api/model/ExposedPortTest.java new file mode 100644 index 00000000..de1d23c2 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/ExposedPortTest.java @@ -0,0 +1,40 @@ +package com.github.dockerjava.api.model; + +import static com.github.dockerjava.api.model.InternetProtocol.DEFAULT; +import static com.github.dockerjava.api.model.InternetProtocol.TCP; +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +public class ExposedPortTest { + + @Test + public void parsePortAndProtocol() { + ExposedPort exposedPort = ExposedPort.parse("80/tcp"); + assertEquals(exposedPort, new ExposedPort(80, TCP)); + } + + @Test + public void parsePortOnly() { + ExposedPort exposedPort = ExposedPort.parse("80"); + assertEquals(exposedPort, new ExposedPort(80, DEFAULT)); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing ExposedPort 'nonsense'") + public void parseInvalidInput() { + ExposedPort.parse("nonsense"); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing ExposedPort 'null'") + public void parseNull() { + ExposedPort.parse(null); + } + + @Test + public void stringify() { + assertEquals(ExposedPort.parse("80/tcp").toString(), "80/tcp"); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/IdentifierTest.java b/src/test/java/com/github/dockerjava/api/model/IdentifierTest.java new file mode 100644 index 00000000..d57c0b57 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/IdentifierTest.java @@ -0,0 +1,39 @@ +package com.github.dockerjava.api.model; + +import junit.framework.TestCase; + + +public class IdentifierTest extends TestCase { + + public void testFromCompoundString() throws Exception { + + Identifier i1 = Identifier.fromCompoundString("10.0.0.1/jim"); + Identifier i2 = Identifier.fromCompoundString("10.0.0.1/jim:123"); + Identifier i3 = Identifier.fromCompoundString("10.0.0.1:123/jim:124"); + Identifier i3A = Identifier.fromCompoundString("10.0.0.1:123/jim:latest"); + + assertTrue(!i1.tag.isPresent()); + assertEquals(i1.repository.name, "10.0.0.1/jim"); + + assertTrue(i2.tag.isPresent()); + assertEquals(i2.tag.get(), "123"); + assertEquals(i2.repository.name, "10.0.0.1/jim"); + + assertTrue(i3.tag.isPresent()); + assertEquals(i3.tag.get(), "124"); + assertEquals(i3.repository.name, "10.0.0.1:123/jim"); + assertEquals(i3.repository.getURL().getPort(), 123); + assertEquals(i3A.tag.get(), "latest"); + + + Identifier i4 = Identifier.fromCompoundString("centos:latest"); + assertTrue(i4.tag.isPresent()); + assertEquals(i4.tag.get(), "latest"); + + Identifier i5 = Identifier.fromCompoundString("busybox"); + assertTrue(!i5.tag.isPresent()); + + Identifier i6 = Identifier.fromCompoundString("10.0.0.1:5000/my-test-image:1234"); + assertEquals(i6.repository.getPath(), "my-test-image"); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/api/model/InternetProtocolTest.java b/src/test/java/com/github/dockerjava/api/model/InternetProtocolTest.java new file mode 100644 index 00000000..ea0b20d7 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/InternetProtocolTest.java @@ -0,0 +1,42 @@ +package com.github.dockerjava.api.model; + +import static com.github.dockerjava.api.model.InternetProtocol.*; +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +public class InternetProtocolTest { + + @Test + public void defaultProtocol() { + assertEquals(InternetProtocol.DEFAULT, TCP); + } + + @Test + public void stringify() { + assertEquals(TCP.toString(), "tcp"); + } + + @Test + public void parseUpperCase() { + assertEquals(InternetProtocol.parse("TCP"), TCP); + } + + @Test + public void parseLowerCase() { + assertEquals(InternetProtocol.parse("tcp"), TCP); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing Protocol.*") + public void parseInvalidInput() { + InternetProtocol.parse("xx"); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing Protocol 'null'") + public void parseNull() { + InternetProtocol.parse(null); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/LinkTest.java b/src/test/java/com/github/dockerjava/api/model/LinkTest.java new file mode 100644 index 00000000..2f6df0c5 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/LinkTest.java @@ -0,0 +1,40 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +public class LinkTest { + + @Test + public void parse() { + Link link = Link.parse("name:alias"); + assertEquals(link.getName(), "name"); + assertEquals(link.getAlias(), "alias"); + } + + @Test + public void parseWithContainerNames() { + Link link = Link.parse("/name:/conatiner/alias"); + assertEquals(link.getName(), "name"); + assertEquals(link.getAlias(), "alias"); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing Link 'nonsense'") + public void parseInvalidInput() { + Link.parse("nonsense"); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing Link 'null'") + public void parseNull() { + Link.parse(null); + } + + @Test + public void stringify() { + assertEquals(Link.parse("name:alias").toString(), "name:alias"); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/PortBindingTest.java b/src/test/java/com/github/dockerjava/api/model/PortBindingTest.java new file mode 100644 index 00000000..2aeb768f --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/PortBindingTest.java @@ -0,0 +1,61 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +import com.github.dockerjava.api.model.Ports.Binding; + +public class PortBindingTest { + + private static final ExposedPort TCP_8080 = ExposedPort.tcp(8080); + + @Test + public void fullDefinition() { + assertEquals(PortBinding.parse("127.0.0.1:80:8080/tcp"), + new PortBinding(new Binding("127.0.0.1", 80), TCP_8080)); + } + + @Test + public void noProtocol() { + assertEquals(PortBinding.parse("127.0.0.1:80:8080"), + new PortBinding(new Binding("127.0.0.1", 80), TCP_8080)); + } + + @Test + public void noHostIp() { + assertEquals(PortBinding.parse("80:8080/tcp"), + new PortBinding(new Binding(80), TCP_8080)); + } + + @Test + public void portsOnly() { + assertEquals(PortBinding.parse("80:8080"), + new PortBinding(new Binding(80), TCP_8080)); + } + + @Test + public void exposedPortOnly() { + assertEquals(PortBinding.parse("8080"), + new PortBinding(new Binding(), TCP_8080)); + } + + @Test + public void dynamicHostPort() { + assertEquals(PortBinding.parse("127.0.0.1::8080"), + new PortBinding(new Binding("127.0.0.1"), TCP_8080)); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing PortBinding 'nonsense'") + public void parseInvalidInput() { + PortBinding.parse("nonsense"); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing PortBinding 'null'") + public void parseNull() { + PortBinding.parse(null); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/Ports_SerializingTest.java b/src/test/java/com/github/dockerjava/api/model/Ports_SerializingTest.java new file mode 100644 index 00000000..9190eefd --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/Ports_SerializingTest.java @@ -0,0 +1,59 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import java.util.Map; + +import org.testng.annotations.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.model.Ports.Binding; + +public class Ports_SerializingTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + private final String jsonWithDoubleBindingForOnePort = + "{\"80/tcp\":[{\"HostIp\":\"10.0.0.1\",\"HostPort\":\"80\"},{\"HostIp\":\"10.0.0.2\",\"HostPort\":\"80\"}]}"; + private final String jsonWithNullBindingForOnePort = "{\"80/tcp\":null}"; + + @Test + public void deserializingPortWithMultipleBindings() throws Exception { + Ports ports = objectMapper.readValue(jsonWithDoubleBindingForOnePort, Ports.class); + Map map = ports.getBindings(); + assertEquals(map.size(), 1); + + Binding[] bindings = map.get(ExposedPort.tcp(80)); + assertEquals(bindings.length, 2); + assertEquals(bindings[0], new Binding("10.0.0.1", 80)); + assertEquals(bindings[1], new Binding("10.0.0.2", 80)); + } + + @Test + public void serializingPortWithMultipleBindings() throws Exception { + Ports ports = new Ports(); + ports.bind(ExposedPort.tcp(80), new Binding("10.0.0.1", 80)); + ports.bind(ExposedPort.tcp(80), new Binding("10.0.0.2", 80)); + assertEquals(objectMapper.writeValueAsString(ports), jsonWithDoubleBindingForOnePort); + } + + @Test + public void serializingEmptyBinding() throws Exception { + Ports ports = new Ports(ExposedPort.tcp(80), new Binding(null, null)); + assertEquals(objectMapper.writeValueAsString(ports), "{\"80/tcp\":[{\"HostIp\":\"\",\"HostPort\":\"\"}]}"); + } + + @Test + public void deserializingPortWithNullBindings() throws Exception { + Ports ports = objectMapper.readValue(jsonWithNullBindingForOnePort, Ports.class); + Map map = ports.getBindings(); + assertEquals(map.size(), 1); + + assertEquals(map.get(ExposedPort.tcp(80)), null); + } + + @Test + public void serializingWithNullBindings() throws Exception { + Ports ports = new Ports(); + ports.bind(ExposedPort.tcp(80), null); + assertEquals(objectMapper.writeValueAsString(ports), jsonWithNullBindingForOnePort); + } +} diff --git a/src/test/java/com/github/dockerjava/api/model/Ports_addBindingsTest.java b/src/test/java/com/github/dockerjava/api/model/Ports_addBindingsTest.java new file mode 100644 index 00000000..18c7f0f0 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/Ports_addBindingsTest.java @@ -0,0 +1,66 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Map; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.model.Ports.Binding; + +/** + * As there may be several {@link Binding}s per {@link ExposedPort}, + * it makes a difference if you add {@link PortBinding}s for the + * same or different {@link ExposedPort}s to {@link Ports}. + * This test verifies that the Map in {@link Ports} is populated + * correctly in both cases. + */ +public class Ports_addBindingsTest { + private static final ExposedPort TCP_80 = ExposedPort.tcp(80); + private static final ExposedPort TCP_90 = ExposedPort.tcp(90); + private static final Binding BINDING_8080 = Ports.Binding(8080); + private static final Binding BINDING_9090 = Ports.Binding(9090); + + private Ports ports; + + @BeforeMethod + public void setup() { + ports = new Ports(); + } + + @Test + public void addTwoBindingsForDifferentExposedPorts() { + ports.add( + new PortBinding(BINDING_8080, TCP_80), + new PortBinding(BINDING_9090, TCP_90)); + + Map bindings = ports.getBindings(); + // two keys with one value each + assertEquals(bindings.size(), 2); + assertEquals(bindings.get(TCP_80), new Binding[] { BINDING_8080 }); + assertEquals(bindings.get(TCP_90), new Binding[] { BINDING_9090 }); + } + + @Test + public void addTwoBindingsForSameExposedPort() { + ports.add( + new PortBinding(BINDING_8080, TCP_80), + new PortBinding(BINDING_9090, TCP_80)); + + Map bindings = ports.getBindings(); + // one key with two values + assertEquals(bindings.size(), 1); + assertEquals(bindings.get(TCP_80), new Binding[] { BINDING_8080, BINDING_9090 }); + } + + @Test + public void addNullBindings() { + ports.add(new PortBinding(null, TCP_80)); + Map bindings = ports.getBindings(); + // one key with two values + assertEquals(bindings.size(), 1); + assertEquals(bindings.get(TCP_80), null); + } +} diff --git a/src/test/java/com/github/dockerjava/api/model/RepositoryTest.java b/src/test/java/com/github/dockerjava/api/model/RepositoryTest.java new file mode 100644 index 00000000..8a40de28 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/RepositoryTest.java @@ -0,0 +1,18 @@ +package com.github.dockerjava.api.model; + +import junit.framework.TestCase; + +public class RepositoryTest extends TestCase { + public void testRepository() throws Exception { + + Repository repo = new Repository("10.0.0.1/jim"); + Repository repo1 = new Repository("10.0.0.1:1234/jim"); + Repository repo2 = new Repository("busybox"); + + assertEquals("jim", repo.getPath()); + assertEquals("jim", repo1.getPath()); + assertEquals("busybox", repo2.getPath()); + + assertEquals(1234, repo1.getURL().getPort()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/api/model/RestartPolicy_ParsingTest.java b/src/test/java/com/github/dockerjava/api/model/RestartPolicy_ParsingTest.java new file mode 100644 index 00000000..04823db2 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/RestartPolicy_ParsingTest.java @@ -0,0 +1,40 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +public class RestartPolicy_ParsingTest { + + @Test + public void noRestart() throws Exception { + assertEquals(RestartPolicy.parse("no"), RestartPolicy.noRestart()); + } + + @Test + public void alwaysRestart() throws Exception { + assertEquals(RestartPolicy.parse("always"), RestartPolicy.alwaysRestart()); + } + + @Test + public void onFailureRestart() throws Exception { + assertEquals(RestartPolicy.parse("on-failure"), RestartPolicy.onFailureRestart(0)); + } + + @Test + public void onFailureRestartWithCount() throws Exception { + assertEquals(RestartPolicy.parse("on-failure:2"), RestartPolicy.onFailureRestart(2)); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing RestartPolicy 'nonsense'") + public void illegalSyntax() throws Exception { + RestartPolicy.parse("nonsense"); + } + + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "Error parsing RestartPolicy 'on-failure:X'") + public void illegalRetryCount() throws Exception { + RestartPolicy.parse("on-failure:X"); + } +} diff --git a/src/test/java/com/github/dockerjava/api/model/RestartPolicy_SerializingTest.java b/src/test/java/com/github/dockerjava/api/model/RestartPolicy_SerializingTest.java new file mode 100644 index 00000000..7b13a395 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/RestartPolicy_SerializingTest.java @@ -0,0 +1,41 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Compares serialization results of various {@link RestartPolicy}s with + * what Docker (as of 1.3.3) actually sends when executing + * docker run --restart xxx. + */ +public class RestartPolicy_SerializingTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test // --restart no + public void noRestart() throws Exception { + String json = objectMapper.writeValueAsString(RestartPolicy.noRestart()); + assertEquals(json, "{\"MaximumRetryCount\":0,\"Name\":\"\"}"); + } + + @Test // --restart always + public void alwaysRestart() throws Exception { + String json = objectMapper.writeValueAsString(RestartPolicy.alwaysRestart()); + assertEquals(json, "{\"MaximumRetryCount\":0,\"Name\":\"always\"}"); + } + + @Test // --restart on-failure + public void onFailureRestart() throws Exception { + String json = objectMapper.writeValueAsString(RestartPolicy.onFailureRestart(0)); + assertEquals(json, "{\"MaximumRetryCount\":0,\"Name\":\"on-failure\"}"); + } + + @Test // --restart on-failure:2 + public void onFailureRestartWithCount() throws Exception { + String json = objectMapper.writeValueAsString(RestartPolicy.onFailureRestart(2)); + assertEquals(json, "{\"MaximumRetryCount\":2,\"Name\":\"on-failure\"}"); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/RestartPolicy_toStringTest.java b/src/test/java/com/github/dockerjava/api/model/RestartPolicy_toStringTest.java new file mode 100644 index 00000000..a52441d6 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/RestartPolicy_toStringTest.java @@ -0,0 +1,25 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class RestartPolicy_toStringTest { + + @DataProvider(name = "input") + public Object[][] restartPolicies() { + return new Object[][] { + { "no" }, + { "always" }, + { "on-failure" }, + { "on-failure:2" } + }; + } + + @Test(dataProvider = "input") + public void serializationWithoutCount(String policy) throws Exception { + assertEquals(RestartPolicy.parse(policy).toString(), policy); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/VolumeBindsTest.java b/src/test/java/com/github/dockerjava/api/model/VolumeBindsTest.java new file mode 100644 index 00000000..e8bafc46 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/VolumeBindsTest.java @@ -0,0 +1,32 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.testng.annotations.Test; + +import java.io.IOException; + +import static org.testng.Assert.*; + +public class VolumeBindsTest { + @Test + public void t() throws IOException { + String s = "{\"/data\":\"/some/path\"}"; + ObjectMapper objectMapper = new ObjectMapper(); + VolumeBinds volumeBinds = objectMapper.readValue(s, VolumeBinds.class); + VolumeBind[] binds = volumeBinds.getBinds(); + assertEquals(binds.length,1); + assertEquals(binds[0].getHostPath(),"/some/path"); + assertEquals(binds[0].getContainerPath(), "/data"); + } + + @Test(expectedExceptions = JsonMappingException.class) + public void t1() throws IOException { + String s = "{\"/data\": {} }"; + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.readValue(s, VolumeBinds.class); + } + + +} \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/api/model/VolumeFrom_SerializingTest.java b/src/test/java/com/github/dockerjava/api/model/VolumeFrom_SerializingTest.java new file mode 100644 index 00000000..b7c3bbff --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/VolumeFrom_SerializingTest.java @@ -0,0 +1,25 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class VolumeFrom_SerializingTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + private final String json = "\"container1:ro\""; + + @Test + public void deserializing() throws Exception { + VolumesFrom volumeFrom = objectMapper.readValue(json, VolumesFrom.class); + assertEquals(volumeFrom, new VolumesFrom("container1", AccessMode.ro)); + } + + @Test + public void serializing() throws Exception { + VolumesFrom volumeFrom = new VolumesFrom("container1", AccessMode.ro); + assertEquals(objectMapper.writeValueAsString(volumeFrom), json); + } + +} diff --git a/src/test/java/com/github/dockerjava/api/model/VolumeTest.java b/src/test/java/com/github/dockerjava/api/model/VolumeTest.java new file mode 100644 index 00000000..7419e5dc --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/VolumeTest.java @@ -0,0 +1,12 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +public class VolumeTest { + @Test + public void getPath() { + assertEquals(new Volume("/path").getPath(), "/path"); + } +} diff --git a/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java b/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java new file mode 100644 index 00000000..802166a0 --- /dev/null +++ b/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java @@ -0,0 +1,190 @@ +package com.github.dockerjava.client; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumeBind; +import com.github.dockerjava.core.DockerClientBuilder; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.TestDockerCmdExecFactory; +import com.google.common.base.Joiner; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.LineIterator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.ITestResult; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.net.DatagramSocket; +import java.net.ServerSocket; +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractDockerClientTest extends Assert { + + public static final Logger LOG = LoggerFactory + .getLogger(AbstractDockerClientTest.class); + + protected DockerClient dockerClient; + + protected TestDockerCmdExecFactory dockerCmdExecFactory = new TestDockerCmdExecFactory(DockerClientBuilder.getDefaultDockerCmdExecFactory()); + + public void beforeTest() { + + LOG.info("======================= BEFORETEST ======================="); + LOG.info("Connecting to Docker server"); + dockerClient = DockerClientBuilder.getInstance(config()) + .withDockerCmdExecFactory(dockerCmdExecFactory) + .build(); + + LOG.info("Pulling image 'busybox'"); + // need to block until image is pulled completely + asString(dockerClient.pullImageCmd("busybox").withTag("latest").exec()); + + assertNotNull(dockerClient); + LOG.info("======================= END OF BEFORETEST =======================\n\n"); + } + + private DockerClientConfig config() { + return config(null); + } + + protected DockerClientConfig config(String password) { + DockerClientConfig.DockerClientConfigBuilder builder = DockerClientConfig.createDefaultConfigBuilder() + .withServerAddress("https://index.docker.io/v1/"); + if (password!=null) { + builder = builder.withPassword(password); + } + return builder + .build(); + } + + public void afterTest() { + LOG.info("======================= END OF AFTERTEST ======================="); + } + + + public void beforeMethod(Method method) { + LOG.info(String + .format("################################## STARTING %s ##################################", + method.getName())); + } + + public void afterMethod(ITestResult result) { + + for (String container : dockerCmdExecFactory.getContainerNames()) { + LOG.info("Cleaning up temporary container {}", container); + + try { + dockerClient.removeContainerCmd(container).withForce().exec(); + } catch (DockerException ignore) { + //ignore.printStackTrace(); + } + } + + for (String image : dockerCmdExecFactory.getImageNames()) { + LOG.info("Cleaning up temporary image with {}", image); + try { + dockerClient.removeImageCmd(image).withForce().exec(); + } catch (DockerException ignore) { + //ignore.printStackTrace(); + } + } + + LOG.info( + "################################## END OF {} ##################################\n", + result.getName()); + } + + protected String asString(InputStream response) { + return consumeAsString(response); + } + + public static String consumeAsString(InputStream response) { + + StringWriter logwriter = new StringWriter(); + + try { + LineIterator itr = IOUtils.lineIterator( + response, "UTF-8"); + + while (itr.hasNext()) { + String line = itr.next(); + logwriter.write(line + (itr.hasNext() ? "\n" : "")); + LOG.info("line: "+line); + } + response.close(); + + return logwriter.toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + IOUtils.closeQuietly(response); + } + } + + // UTIL + + /** + * Checks to see if a specific port is available. + * + * @param port + * the port to check for availability + */ + public static boolean available(int port) { + if (port < 1100 || port > 60000) { + throw new IllegalArgumentException("Invalid start port: " + port); + } + + ServerSocket ss = null; + DatagramSocket ds = null; + try { + ss = new ServerSocket(port); + ss.setReuseAddress(true); + ds = new DatagramSocket(port); + ds.setReuseAddress(true); + return true; + } catch (IOException ignored) { + } finally { + if (ds != null) { + ds.close(); + } + + if (ss != null) { + try { + ss.close(); + } catch (IOException e) { + /* should not be thrown */ + } + } + } + + return false; + } + + /** + * Asserts that {@link InspectContainerResponse#getVolumes()} (.Volumes) + * has {@link VolumeBind}s for the given {@link Volume}s + */ + public static void assertContainerHasVolumes(InspectContainerResponse inspectContainerResponse, + Volume ... expectedVolumes) { + VolumeBind[] volumeBinds = inspectContainerResponse.getVolumes(); + LOG.info("Inspect .Volumes = [{}]", Joiner.on(", ").join(volumeBinds)); + + List volumes = new ArrayList(); + for (VolumeBind bind : volumeBinds) { + volumes.add(new Volume(bind.getContainerPath())); + } + assertThat(volumes, contains(expectedVolumes)); + } + +} diff --git a/src/test/java/com/github/dockerjava/client/DockerClientTest.java b/src/test/java/com/github/dockerjava/client/DockerClientTest.java new file mode 100644 index 00000000..e5bef57d --- /dev/null +++ b/src/test/java/com/github/dockerjava/client/DockerClientTest.java @@ -0,0 +1,73 @@ +package com.github.dockerjava.client; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +import java.lang.reflect.Method; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +/** + * Unit test for DockerClient. + * + * @author Konstantin Pelykh (kpelykh@gmail.com) + */ +@Test(groups = "integration") +public class DockerClientTest extends AbstractDockerClientTest { + public static final Logger LOG = LoggerFactory + .getLogger(DockerClientTest.class); + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + + @Test + public void testRunShlex() throws DockerException { + + String[] commands = new String[] { + "true", + "echo \"The Young Descendant of Tepes & Septette for the Dead Princess\"", + "echo -n 'The Young Descendant of Tepes & Septette for the Dead Princess'", + "/bin/sh -c echo Hello World", "/bin/sh -c echo 'Hello World'", + "echo 'Night of Nights'", "true && echo 'Night of Nights'" }; + + for (String command : commands) { + LOG.info("Running command: [{}]", command); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd(commands).exec(); + dockerClient.startContainerCmd(container.getId()); + + int exitcode = dockerClient.waitContainerCmd(container.getId()).exec(); + assertThat(exitcode, equalTo(0)); + } + } + + +} diff --git a/src/test/java/com/github/dockerjava/core/AuthConfigFileTest.java b/src/test/java/com/github/dockerjava/core/AuthConfigFileTest.java new file mode 100644 index 00000000..e1c1b6f8 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/AuthConfigFileTest.java @@ -0,0 +1,95 @@ +/** + * Copyright (C) 2014 SignalFuse, Inc. + */ +package com.github.dockerjava.core; + +import java.io.File; +import java.io.IOException; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.model.AuthConfig; + +public class AuthConfigFileTest { + + private final File FILESROOT = new File(Thread.currentThread().getContextClassLoader() + .getResource("testAuthConfigFile").getFile()); + + @Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "The Auth Config file is empty") + public void emptyFile() throws IOException { + runTest("emptyFile"); + } + + @Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "The Auth Config file is empty") + public void tooSmallFile() throws IOException { + runTest("tooSmallFile"); + } + + @Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "Invalid auth configuration file") + public void invalidJsonInvalidAuth() throws IOException { + runTest("invalidJsonInvalidAuth"); + } + + @Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "Invalid Auth config file") + public void invalidLegacyAuthLine() throws IOException { + runTest("invalidLegacyAuthLine"); + } + + @Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "Invalid auth configuration file") + public void invalidLegacyInvalidAuth() throws IOException { + runTest("invalidLegacyInvalidAuth"); + } + + @Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "Invalid Auth config file") + public void invalidLegacyEmailLine() throws IOException { + runTest("invalidLegacyEmailLine"); + } + + @Test + public void validJson() throws IOException { + AuthConfig authConfig1 = new AuthConfig(); + authConfig1.setEmail("foo@example.com"); + authConfig1.setUsername("foo"); + authConfig1.setPassword("bar"); + authConfig1.setServerAddress("quay.io"); + + AuthConfig authConfig2 = new AuthConfig(); + authConfig2.setEmail("moo@example.com"); + authConfig2.setUsername("foo1"); + authConfig2.setPassword("bar1"); + authConfig2.setServerAddress(AuthConfig.DEFAULT_SERVER_ADDRESS); + + AuthConfigFile expected = new AuthConfigFile(); + expected.addConfig(authConfig1); + expected.addConfig(authConfig2); + + Assert.assertEquals(runTest("validJson"), expected); + + } + + @Test + public void validLegacy() throws IOException { + AuthConfig authConfig = new AuthConfig(); + authConfig.setEmail("foo@example.com"); + authConfig.setUsername("foo"); + authConfig.setPassword("bar"); + authConfig.setServerAddress(AuthConfig.DEFAULT_SERVER_ADDRESS); + + AuthConfigFile expected = new AuthConfigFile(); + expected.addConfig(authConfig); + + Assert.assertEquals(runTest("validLegacy"), expected); + } + + @Test + public void nonExistent() throws IOException { + AuthConfigFile expected = new AuthConfigFile(); + Assert.assertEquals(runTest("idontexist"), expected); + } + + private AuthConfigFile runTest(String testFileName) throws IOException { + return AuthConfigFile.loadConfig(new File(FILESROOT, testFileName)); + } + +} diff --git a/src/test/java/com/github/dockerjava/core/CompressArchiveUtilTest.java b/src/test/java/com/github/dockerjava/core/CompressArchiveUtilTest.java new file mode 100644 index 00000000..5212fe50 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/CompressArchiveUtilTest.java @@ -0,0 +1,59 @@ +package com.github.dockerjava.core; + +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class CompressArchiveUtilTest { + + @Test + public void testExecutableFlagIsPreserved() throws Exception { + File executableFile = createExecutableFile(); + File archive = CompressArchiveUtil.archiveTARFiles(executableFile.getParentFile(), asList(executableFile), "archive"); + File expectedFile = extractFileByName(archive, "executableFile.sh.result"); + + assertThat("should be executable", expectedFile.canExecute()); + } + + private File createExecutableFile() throws IOException { + File baseDir = new File(FileUtils.getTempDirectoryPath()); + File executableFile = new File(baseDir, "executableFile.sh"); + executableFile.createNewFile(); + executableFile.setExecutable(true); + assertThat(executableFile.canExecute(), is(true)); + return executableFile; + } + + private File extractFileByName(File archive, String filenameToExtract) throws IOException { + File baseDir = new File(FileUtils.getTempDirectoryPath()); + File expectedFile = new File(baseDir, filenameToExtract); + expectedFile.delete(); + assertThat(expectedFile.exists(), is(false)); + + TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(new FileInputStream(archive)); + TarArchiveEntry entry; + while ((entry = tarArchiveInputStream.getNextTarEntry()) != null) { + String individualFiles = entry.getName(); + // there should be only one file in this archive + assertThat(individualFiles, equalTo("executableFile.sh")); + IOUtils.copy(tarArchiveInputStream, new FileOutputStream(expectedFile)); + if ((entry.getMode() & 0755) == 0755) { + expectedFile.setExecutable(true); + } + } + tarArchiveInputStream.close(); + return expectedFile; + } +} diff --git a/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java b/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java new file mode 100644 index 00000000..64ab912d --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java @@ -0,0 +1,188 @@ +package com.github.dockerjava.core; + +import com.github.dockerjava.api.model.AuthConfig; +import org.testng.annotations.Test; + +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import static org.testng.Assert.assertEquals; + +public class DockerClientConfigTest { + + public static final DockerClientConfig EXAMPLE_CONFIG = newExampleConfig(); + + private static DockerClientConfig newExampleConfig() { + return new DockerClientConfig(URI.create("http://foo"), "bar", "baz", "qux", "blam", "wham", "flam", 877, false, false, new LocalDirectorySSLConfig("flim"), 20, 2); + } + + @Test + public void string() throws Exception { + assertEquals("DockerClientConfig{uri=http://foo, version='bar', username='baz', password='qux', email='blam', serverAddress='wham', dockerCfgPath='flam', sslConfig='LocalDirectorySSLConfig{dockerCertPath=flim}', readTimeout=877, loggingFilterEnabled=false, followRedirectsFilterEnabled=false}", + EXAMPLE_CONFIG.toString()); + } + + @Test + public void equals() throws Exception { + assertEquals(EXAMPLE_CONFIG, newExampleConfig()); + } + + @Test + public void environmentDockerHost() throws Exception { + + // given docker host in env + Map env = new HashMap(); + env.put("DOCKER_HOST", "tcp://baz:8768"); + // and it looks to be SSL disabled + env.remove("DOCKER_CERT_PATH"); + + // when you build a config + DockerClientConfig config = buildConfig(env, new Properties()); + + // then the URL is that value with "http" instead of "tcp" + assertEquals(config.getUri(), URI.create("http://baz:8768")); + } + + @Test + public void environmentDockerHostHttpsAutoDetectByCertPath() throws Exception { + + // given docker host in env + Map env = new HashMap(System.getenv()); + env.put("DOCKER_HOST", "tcp://bar:8768"); + // and it looks to be SSL enabled + env.put("DOCKER_CERT_PATH", "any value"); + + // when you build a config + DockerClientConfig config = buildConfig(env, new Properties()); + + // then the URL is that value with "tcp" changed to "https" + assertEquals(config.getUri(), URI.create("https://bar:8768")); + } + + @Test + public void environmentDockerHostHttpsAutoDetectByTlsVerify() throws Exception { + + // given docker host in env + Map env = new HashMap(System.getenv()); + env.put("DOCKER_HOST", "tcp://bar:8768"); + // and it looks to be SSL enabled + env.put("DOCKER_TLS_VERIFY", "1"); + + // when you build a config + DockerClientConfig config = buildConfig(env, new Properties()); + + // then the URL is that value with "tcp" changed to "https" + assertEquals(config.getUri(), URI.create("https://bar:8768")); + } + + @Test + public void environmentDockerHostWithInvalidTlsVerify() throws Exception { + + // given docker host in env + Map env = new HashMap(System.getenv()); + env.put("DOCKER_HOST", "tcp://bar:8768"); + // and it looks to be SSL disabled + env.remove("DOCKER_CERT_PATH"); + // and it has an invalid TLS_VERIFY value + env.put("DOCKER_TLS_VERIFY", "any value different from '1'"); + + // when you build a config + DockerClientConfig config = buildConfig(env, new Properties()); + + // then the URL is that value with "tcp" changed to "https" + assertEquals(config.getUri(), URI.create("http://bar:8768")); + } + + @Test + public void environmentDockerHostWithInvalidTlsVerifyButWithCertPath() throws Exception { + + // given docker host in env + Map env = new HashMap(System.getenv()); + env.put("DOCKER_HOST", "tcp://bar:8768"); + // and it looks to be SSL enabled + env.put("DOCKER_CERT_PATH", "any value"); + // and it has an invalid TLS_VERIFY value + env.put("DOCKER_TLS_VERIFY", "any value different from '1'"); + + // when you build a config + DockerClientConfig config = buildConfig(env, new Properties()); + + // then the URL is that value with "tcp" changed to "https" + assertEquals(config.getUri(), URI.create("https://bar:8768")); + } + + @Test + public void environment() throws Exception { + + // given a default config in env properties + Map env = new HashMap(); + env.put("DOCKER_URL", "http://foo"); + env.put("DOCKER_VERSION", "bar"); + env.put("DOCKER_USERNAME", "baz"); + env.put("DOCKER_PASSWORD", "qux"); + env.put("DOCKER_EMAIL", "blam"); + env.put("DOCKER_SERVER_ADDRESS", "wham"); + env.put("DOCKER_CERT_PATH", "flim"); + env.put("DOCKER_CFG_PATH", "flam"); + env.put("DOCKER_READ_TIMEOUT", "877"); + env.put("DOCKER_LOGGING_FILTER_ENABLED", "false"); + + // when you build a config + DockerClientConfig config = buildConfig(env, new Properties()); + + // then we get the example object + assertEquals(config, EXAMPLE_CONFIG); + } + + private DockerClientConfig buildConfig(Map env, Properties systemProperties) { + return DockerClientConfig.createDefaultConfigBuilder(env, systemProperties).build(); + } + + @Test + public void defaults() throws Exception { + + // given default cert path + Properties systemProperties = new Properties(); + systemProperties.setProperty("user.name", "someUserName"); + systemProperties.setProperty("user.home", "someHomeDir"); + + // when you build config + DockerClientConfig config = buildConfig(Collections.emptyMap(), systemProperties); + + // then the cert path is as expected + assertEquals(config.getUri(), URI.create("https://localhost:2376")); + assertEquals(config.getUsername(), "someUserName"); + assertEquals(config.getServerAddress(), AuthConfig.DEFAULT_SERVER_ADDRESS); + assertEquals(config.getVersion(), null); + assertEquals(config.isLoggingFilterEnabled(), true); + assertEquals(config.getDockerCfgPath(), "someHomeDir/.dockercfg"); + assertEquals( ((LocalDirectorySSLConfig)config.getSslConfig()).getDockerCertPath(), "someHomeDir/.docker"); + } + + @Test + public void systemProperties() throws Exception { + + // given system properties based on the example + Properties systemProperties = new Properties(); + systemProperties.setProperty("docker.io.url", "http://foo"); + systemProperties.setProperty("docker.io.version", "bar"); + systemProperties.setProperty("docker.io.username", "baz"); + systemProperties.setProperty("docker.io.password", "qux"); + systemProperties.setProperty("docker.io.email", "blam"); + systemProperties.setProperty("docker.io.serverAddress", "wham"); + systemProperties.setProperty("docker.io.dockerCertPath", "flim"); + systemProperties.setProperty("docker.io.dockerCfgPath", "flam"); + systemProperties.setProperty("docker.io.readTimeout", "877"); + systemProperties.setProperty("docker.io.enableLoggingFilter", "false"); + + // when you build new config + DockerClientConfig config = buildConfig(Collections.emptyMap(), systemProperties); + + // then it is the same as the example + assertEquals(config, EXAMPLE_CONFIG); + + } +} \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java b/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java new file mode 100644 index 00000000..2a961e3b --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java @@ -0,0 +1,36 @@ +package com.github.dockerjava.core; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.AssertJUnit.fail; + +public class DockerClientImplTest { + + @Test + public void configuredInstanceAuthConfig() throws Exception { + // given a config with null serverAddress + DockerClientConfig dockerClientConfig = new DockerClientConfig(null, null, "", "", "", null, null, 0, false, false, null, 20, 2); + DockerClientImpl dockerClient = DockerClientImpl.getInstance(dockerClientConfig); + + // when we get the auth config + try { + dockerClient.authConfig(); + fail(); + } catch (NullPointerException e) { + // then we get a NPE with expected message + assertEquals(e.getMessage(), "Configured serverAddress is null."); + } + } + + @Test + public void defaultInstanceAuthConfig() throws Exception { + // given a default client + DockerClientImpl dockerClient = DockerClientImpl.getInstance(); + + // when we get the auth config + dockerClient.authConfig(); + + // then we do not get an exception + } +} \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/core/GoLangFileMatchTest.java b/src/test/java/com/github/dockerjava/core/GoLangFileMatchTest.java new file mode 100644 index 00000000..215926a5 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/GoLangFileMatchTest.java @@ -0,0 +1,121 @@ +/** + * Copyright (C) 2014 SignalFuse, Inc. + */ +package com.github.dockerjava.core; + +import java.io.IOException; + +import junit.framework.Assert; + +import org.apache.commons.io.FilenameUtils; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class GoLangFileMatchTest { + + @Test(dataProvider = "getTestData") + public void testMatch(MatchTestCase testCase) throws IOException { + String pattern = testCase.pattern; + String s = testCase.s; + if (GoLangFileMatch.IS_WINDOWS) { + if (pattern.indexOf('\\') >= 0) { + // no escape allowed on windows. + return; + } + pattern = FilenameUtils.normalize(pattern); + s = FilenameUtils.normalize(s); + } + try { + boolean matched = GoLangFileMatch.match(pattern, s); + if (testCase.expectException) { + Assert.fail("Expected GoFileMatchException"); + } + Assert.assertEquals(testCase.matches, matched); + } catch (GoLangFileMatchException e) { + if (!testCase.expectException) { + throw e; + } + } + } + + @DataProvider + public Object[][] getTestData() { + return new Object[][] { + new Object[] { new MatchTestCase("abc", "abc", true, false) }, + new Object[] { new MatchTestCase("*", "abc", true, false) }, + new Object[] { new MatchTestCase("*c", "abc", true, false) }, + new Object[] { new MatchTestCase("a*", "a", true, false) }, + new Object[] { new MatchTestCase("a*", "abc", true, false) }, + new Object[] { new MatchTestCase("a*", "ab/c", false, false) }, + new Object[] { new MatchTestCase("a*/b", "abc/b", true, false) }, + new Object[] { new MatchTestCase("a*/b", "a/c/b", false, false) }, + new Object[] { new MatchTestCase("a*b*c*d*e*/f", "axbxcxdxe/f", true, false) }, + new Object[] { new MatchTestCase("a*b*c*d*e*/f", "axbxcxdxexxx/f", true, false) }, + new Object[] { new MatchTestCase("a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, false) }, + new Object[] { new MatchTestCase("a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, false) }, + new Object[] { new MatchTestCase("a*b?c*x", "abxbbxdbxebxczzx", true, false) }, + new Object[] { new MatchTestCase("a*b?c*x", "abxbbxdbxebxczzy", false, false) }, + new Object[] { new MatchTestCase("ab[c]", "abc", true, false) }, + new Object[] { new MatchTestCase("ab[b-d]", "abc", true, false) }, + new Object[] { new MatchTestCase("ab[e-g]", "abc", false, false) }, + new Object[] { new MatchTestCase("ab[^c]", "abc", false, false) }, + new Object[] { new MatchTestCase("ab[^b-d]", "abc", false, false) }, + new Object[] { new MatchTestCase("ab[^e-g]", "abc", true, false) }, + new Object[] { new MatchTestCase("a\\*b", "a*b", true, false) }, + new Object[] { new MatchTestCase("a\\*b", "ab", false, false) }, + new Object[] { new MatchTestCase("a?b", "a☺b", true, false) }, + new Object[] { new MatchTestCase("a[^a]b", "a☺b", true, false) }, + new Object[] { new MatchTestCase("a???b", "a☺b", false, false) }, + new Object[] { new MatchTestCase("a[^a][^a][^a]b", "a☺b", false, false) }, + new Object[] { new MatchTestCase("[a-ζ]*", "α", true, false) }, + new Object[] { new MatchTestCase("*[a-ζ]", "A", false, false) }, + new Object[] { new MatchTestCase("a?b", "a/b", false, false) }, + new Object[] { new MatchTestCase("a*b", "a/b", false, false) }, + new Object[] { new MatchTestCase("[\\]a]", "]", true, false) }, + new Object[] { new MatchTestCase("[\\-]", "-", true, false) }, + new Object[] { new MatchTestCase("[x\\-]", "x", true, false) }, + new Object[] { new MatchTestCase("[x\\-]", "-", true, false) }, + new Object[] { new MatchTestCase("[x\\-]", "z", false, false) }, + new Object[] { new MatchTestCase("[\\-x]", "x", true, false) }, + new Object[] { new MatchTestCase("[\\-x]", "-", true, false) }, + new Object[] { new MatchTestCase("[\\-x]", "a", false, false) }, + new Object[] { new MatchTestCase("[]a]", "]", false, true) }, + new Object[] { new MatchTestCase("[-]", "-", false, true) }, + new Object[] { new MatchTestCase("[x-]", "x", false, true) }, + new Object[] { new MatchTestCase("[x-]", "-", false, true) }, + new Object[] { new MatchTestCase("[x-]", "z", false, true) }, + new Object[] { new MatchTestCase("[-x]", "x", false, true) }, + new Object[] { new MatchTestCase("[-x]", "-", false, true) }, + new Object[] { new MatchTestCase("[-x]", "a", false, true) }, + new Object[] { new MatchTestCase("\\", "a", false, true) }, + new Object[] { new MatchTestCase("[a-b-c]", "a", false, true) }, + new Object[] { new MatchTestCase("[", "a", false, true) }, + new Object[] { new MatchTestCase("[^", "a", false, true) }, + new Object[] { new MatchTestCase("[^bc", "a", false, true) }, + new Object[] { new MatchTestCase("a[", "a", false, false) }, + new Object[] { new MatchTestCase("a[", "ab", false, true) }, + new Object[] { new MatchTestCase("*x", "xxx", true, false) } }; + } + + private final class MatchTestCase { + private final String pattern; + private final String s; + private final boolean matches; + private final boolean expectException; + + public MatchTestCase(String pattern, String s, boolean matches, boolean expectException) { + super(); + this.pattern = pattern; + this.s = s; + this.matches = matches; + this.expectException = expectException; + } + + @Override + public String toString() { + return "MatchTestCase [pattern=" + pattern + ", s=" + s + ", matches=" + matches + + ", expectException=" + expectException + "]"; + } + } + +} diff --git a/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java b/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java new file mode 100644 index 00000000..82297cd2 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java @@ -0,0 +1,232 @@ +package com.github.dockerjava.core; + +import java.io.IOException; +import java.io.InputStream; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; + +import com.github.dockerjava.api.command.*; +import com.github.dockerjava.api.command.AuthCmd.Exec; +import com.github.dockerjava.jaxrs.BuildImageCmdExec; + +/** + * Special {@link DockerCmdExecFactory} implementation that collects container + * and image creations while test execution for the purpose of automatically + * cleanup. + * + * @author marcus + * + */ +public class TestDockerCmdExecFactory implements DockerCmdExecFactory { + + private List containerNames = new ArrayList(); + + private List imageNames = new ArrayList(); + + private DockerCmdExecFactory delegate; + + public TestDockerCmdExecFactory(DockerCmdExecFactory delegate) { + this.delegate = delegate; + } + + public void init(DockerClientConfig dockerClientConfig) { + delegate.init(dockerClientConfig); + } + + public void close() throws IOException { + delegate.close(); + } + + public CreateContainerCmd.Exec createCreateContainerCmdExec() { + return new CreateContainerCmd.Exec() { + public CreateContainerResponse exec(CreateContainerCmd command) { + CreateContainerResponse createContainerResponse = delegate + .createCreateContainerCmdExec().exec(command); + containerNames.add(createContainerResponse.getId()); + return createContainerResponse; + } + }; + } + + public RemoveContainerCmd.Exec createRemoveContainerCmdExec() { + return new RemoveContainerCmd.Exec() { + public Void exec(RemoveContainerCmd command) { + delegate.createRemoveContainerCmdExec().exec(command); + containerNames.remove(command.getContainerId()); + return null; + } + }; + } + + public CreateImageCmd.Exec createCreateImageCmdExec() { + return new CreateImageCmd.Exec() { + public CreateImageResponse exec(CreateImageCmd command) { + CreateImageResponse createImageResponse = delegate + .createCreateImageCmdExec().exec(command); + imageNames.add(createImageResponse.getId()); + return createImageResponse; + } + }; + } + + public RemoveImageCmd.Exec createRemoveImageCmdExec() { + return new RemoveImageCmd.Exec() { + public Void exec(RemoveImageCmd command) { + delegate.createRemoveImageCmdExec().exec(command); + imageNames.remove(command.getImageId()); + return null; + } + }; + } + + public BuildImageCmd.Exec createBuildImageCmdExec() { + return new BuildImageCmd.Exec() { + public BuildImageCmd.Response exec(BuildImageCmd command) { + // can't detect image id here so tagging it + String tag = command.getTag(); + if (tag == null || "".equals(tag.trim())) { + tag = "" + new SecureRandom().nextInt(Integer.MAX_VALUE); + command.withTag(tag); + } + InputStream inputStream = delegate.createBuildImageCmdExec() + .exec(command); + imageNames.add(tag); + return new BuildImageCmdExec.ResponseImpl(inputStream); + } + }; + } + + public Exec createAuthCmdExec() { + return delegate.createAuthCmdExec(); + } + + public InfoCmd.Exec createInfoCmdExec() { + return delegate.createInfoCmdExec(); + } + + public PingCmd.Exec createPingCmdExec() { + return delegate.createPingCmdExec(); + } + + public ExecCreateCmd.Exec createExecCmdExec() { + return delegate.createExecCmdExec(); + } + + public VersionCmd.Exec createVersionCmdExec() { + return delegate.createVersionCmdExec(); + } + + public PullImageCmd.Exec createPullImageCmdExec() { + return delegate.createPullImageCmdExec(); + } + + public PushImageCmd.Exec createPushImageCmdExec() { + return delegate.createPushImageCmdExec(); + } + + public SaveImageCmd.Exec createSaveImageCmdExec() { + return delegate.createSaveImageCmdExec(); + } + + public SearchImagesCmd.Exec createSearchImagesCmdExec() { + return delegate.createSearchImagesCmdExec(); + } + + public ListImagesCmd.Exec createListImagesCmdExec() { + return delegate.createListImagesCmdExec(); + } + + public InspectImageCmd.Exec createInspectImageCmdExec() { + return delegate.createInspectImageCmdExec(); + } + + public ListContainersCmd.Exec createListContainersCmdExec() { + return delegate.createListContainersCmdExec(); + } + + public StartContainerCmd.Exec createStartContainerCmdExec() { + return delegate.createStartContainerCmdExec(); + } + + public InspectContainerCmd.Exec createInspectContainerCmdExec() { + return delegate.createInspectContainerCmdExec(); + } + + public WaitContainerCmd.Exec createWaitContainerCmdExec() { + return delegate.createWaitContainerCmdExec(); + } + + public AttachContainerCmd.Exec createAttachContainerCmdExec() { + return delegate.createAttachContainerCmdExec(); + } + + public ExecStartCmd.Exec createExecStartCmdExec() { + return delegate.createExecStartCmdExec(); + } + + public InspectExecCmd.Exec createInspectExecCmdExec() { + return delegate.createInspectExecCmdExec(); + } + + public LogContainerCmd.Exec createLogContainerCmdExec() { + return delegate.createLogContainerCmdExec(); + } + + public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec() { + return delegate.createCopyFileFromContainerCmdExec(); + } + + public StopContainerCmd.Exec createStopContainerCmdExec() { + return delegate.createStopContainerCmdExec(); + } + + public ContainerDiffCmd.Exec createContainerDiffCmdExec() { + return delegate.createContainerDiffCmdExec(); + } + + public KillContainerCmd.Exec createKillContainerCmdExec() { + return delegate.createKillContainerCmdExec(); + } + + public RestartContainerCmd.Exec createRestartContainerCmdExec() { + return delegate.createRestartContainerCmdExec(); + } + + public CommitCmd.Exec createCommitCmdExec() { + return delegate.createCommitCmdExec(); + } + + public TopContainerCmd.Exec createTopContainerCmdExec() { + return delegate.createTopContainerCmdExec(); + } + + public TagImageCmd.Exec createTagImageCmdExec() { + return delegate.createTagImageCmdExec(); + } + + public PauseContainerCmd.Exec createPauseContainerCmdExec() { + return delegate.createPauseContainerCmdExec(); + } + + public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec() { + return delegate.createUnpauseContainerCmdExec(); + } + + public EventsCmd.Exec createEventsCmdExec() { + return delegate.createEventsCmdExec(); + } + + public List getContainerNames() { + return new ArrayList(containerNames); + } + + public List getImageNames() { + return new ArrayList(imageNames); + } + + public StatsCmd.Exec createStatsCmdExec() { + return delegate.createStatsCmdExec(); + } + +} diff --git a/src/test/java/com/github/dockerjava/core/command/AuthCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/AuthCmdImplTest.java new file mode 100644 index 00000000..95011149 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/AuthCmdImplTest.java @@ -0,0 +1,53 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.UnauthorizedException; +import com.github.dockerjava.api.model.AuthResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; +import com.github.dockerjava.core.DockerClientBuilder; +import org.testng.ITestResult; +import org.testng.annotations.*; + +import java.lang.reflect.Method; + +@Test(groups = "integration") +public class AuthCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void testAuth() throws Exception { + AuthResponse response = dockerClient.authCmd().exec(); + + assertEquals(response.getStatus(), "Login Succeeded"); + } + + @Test + public void testAuthInvalid() throws Exception { + + try { + DockerClientBuilder.getInstance(config("garbage")).build().authCmd().exec(); + fail("Expected a UnauthorizedException caused by a bad password."); + } catch (UnauthorizedException e) { + assertEquals(e.getMessage(), "Wrong login/password, please try again\n"); + } + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java new file mode 100644 index 00000000..c9bd51b9 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java @@ -0,0 +1,284 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.UUID; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.TrueFileFilter; +import org.apache.commons.lang.StringUtils; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerClientException; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.BuildImageCmd; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.InspectImageResponse; +import com.github.dockerjava.api.model.EventStreamItem; +import com.github.dockerjava.client.AbstractDockerClientTest; +import com.github.dockerjava.core.CompressArchiveUtil; + +@Test(groups = "integration") +public class BuildImageCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void testNginxDockerfileBuilder() { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("nginx").getFile()); + + InputStream response = dockerClient.buildImageCmd(baseDir).withNoCache().exec(); + + String fullLog = asString(response); + assertThat(fullLog, containsString("Successfully built")); + + String imageId = StringUtils.substringBetween(fullLog, + "Successfully built ", "\\n\"}").trim(); + + InspectImageResponse inspectImageResponse = dockerClient + .inspectImageCmd(imageId).exec(); + assertThat(inspectImageResponse, not(nullValue())); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + assertThat(inspectImageResponse.getAuthor(), + equalTo("Guillaume J. Charmes \"guillaume@dotcloud.com\"")); + } + + + @Test(groups = "ignoreInCircleCi") + public void testNonstandard1() { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("nonstandard/subdirectory/Dockerfile-nonstandard").getFile()); + + InputStream response = dockerClient.buildImageCmd(baseDir).withNoCache().exec(); + + String fullLog = asString(response); + assertThat(fullLog, containsString("Successfully built")); + } + + @Test(groups = "ignoreInCircleCi") + public void testNonstandard2() { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("nonstandard").getFile()); + File dockerFile = new File(Thread.currentThread().getContextClassLoader() + .getResource("nonstandard/subdirectory/Dockerfile-nonstandard").getFile()); + + + InputStream response = dockerClient.buildImageCmd() + .withBaseDirectory(baseDir) + .withDockerfile(dockerFile) + .withNoCache().exec(); + + String fullLog = asString(response); + assertThat(fullLog, containsString("Successfully built")); + } + + @Test + public void testDockerBuilderFromTar() throws IOException { + File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFile").getFile()); + Collection files = FileUtils.listFiles(baseDir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); + File tarFile = CompressArchiveUtil.archiveTARFiles(baseDir, files, UUID.randomUUID().toString()); + String response = dockerfileBuild(new FileInputStream(tarFile)); + assertThat(response, containsString("Successfully executed testrun.sh")); + } + + @Test + public void testDockerBuilderAddUrl() { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("testAddUrl").getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("Docker")); + } + + @Test + public void testDockerBuilderAddFileInSubfolder() throws DockerException, + IOException { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("testAddFileInSubfolder").getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("Successfully executed testrun.sh")); + } + + @Test + public void testDockerBuilderAddFilesViaWildcard() throws DockerException, + IOException { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("testAddFilesViaWildcard").getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("Successfully executed testinclude1.sh")); + assertThat(response, not(containsString("Successfully executed testinclude2.sh"))); + } + + @Test + public void testDockerBuilderAddFolder() throws DockerException, + IOException { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("testAddFolder").getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("Successfully executed testAddFolder.sh")); + } + + @Test + public void testDockerBuilderEnv() throws DockerException, + IOException { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("testEnv").getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("Successfully executed testrun.sh")); + } + + private String dockerfileBuild(InputStream tarInputStream) { + return execBuild(dockerClient.buildImageCmd().withTarInputStream(tarInputStream)); + } + + private String dockerfileBuild(File baseDir) { + return execBuild(dockerClient.buildImageCmd(baseDir)); + } + + private String execBuild(BuildImageCmd buildImageCmd) { + // Build image + InputStream response = buildImageCmd.withNoCache().exec(); + + String fullLog = asString(response); + assertThat(fullLog, containsString("Successfully built")); + + String imageId = StringUtils.substringBetween(fullLog, "Successfully built ", "\\n\"}").trim(); + + // Create container based on image + CreateContainerResponse container = dockerClient.createContainerCmd(imageId).exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + dockerClient.waitContainerCmd(container.getId()).exec(); + + // Log container + InputStream logResponse = logContainer(container.getId()); + + //assertThat(asString(logResponse), containsString(expectedText)); + + return asString(logResponse); + } + + private InputStream logContainer(String containerId) { + return dockerClient.logContainerCmd(containerId).withStdErr().withStdOut().exec(); + } + + @Test(expectedExceptions={DockerClientException.class}) + public void testDockerfileIgnored() { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("testDockerfileIgnored").getFile()); + dockerClient.buildImageCmd(baseDir).withNoCache().exec(); + } + + @Test(expectedExceptions={DockerClientException.class}) + public void testInvalidDockerIgnorePattern() { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("testInvalidDockerignorePattern").getFile()); + dockerClient.buildImageCmd(baseDir).withNoCache().exec(); + } + + @Test(groups = "ignoreInCircleCi") + public void testDockerIgnore() throws DockerException, + IOException { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("testDockerignore").getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("/tmp/a/a /tmp/a/c /tmp/a/d")); + } + + @Test + public void testNetCatDockerfileBuilder() throws InterruptedException, IOException { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("netcat").getFile()); + + Iterable response = dockerClient.buildImageCmd(baseDir).withNoCache().exec().getItems(); + + String imageId = null; + + for(EventStreamItem item : response) { + String text = item.getStream(); + if( text.startsWith("Successfully built ")) { + imageId = StringUtils.substringBetween(text, + "Successfully built ", "\n").trim(); + } + } + + assertNotNull(imageId, "Not successful in build"); + + + InspectImageResponse inspectImageResponse = dockerClient + .inspectImageCmd(imageId).exec(); + assertThat(inspectImageResponse, not(nullValue())); + assertThat(inspectImageResponse.getId(), not(nullValue())); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + CreateContainerResponse container = dockerClient.createContainerCmd( + inspectImageResponse.getId()).exec(); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getId(), notNullValue()); + assertThat(inspectContainerResponse.getNetworkSettings().getPorts(), + notNullValue()); + + // No use as such if not running on the server +// for (Ports.Port p : inspectContainerResponse.getNetworkSettings().getPorts().getAllPorts()) { +// int port = Integer.valueOf(p.getHostPort()); +// LOG.info("Checking port {} is open", port); +// assertThat(available(port), is(false)); +// } + dockerClient.stopContainerCmd(container.getId()).withTimeout(0).exec(); + + } + + @Test + public void testAddAndCopySubstitution () throws DockerException, IOException { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("testENVSubstitution").getFile()); + String response = dockerfileBuild(baseDir); + assertThat(response, containsString("testENVSubstitution successfully completed")); + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/CommitCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/CommitCmdImplTest.java new file mode 100644 index 00000000..ea5f6128 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/CommitCmdImplTest.java @@ -0,0 +1,87 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectImageResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class CommitCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void commit() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("touch", "/test").exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + LOG.info("Commiting container: {}", container.toString()); + String imageId = dockerClient + .commitCmd(container.getId()).exec(); + + InspectImageResponse inspectImageResponse = dockerClient + .inspectImageCmd(imageId).exec(); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + assertThat(inspectImageResponse, + hasField("container", startsWith(container.getId()))); + assertThat(inspectImageResponse.getContainerConfig().getImage(), + equalTo("busybox")); + + InspectImageResponse busyboxImg = dockerClient.inspectImageCmd("busybox").exec(); + + assertThat(inspectImageResponse.getParent(), + equalTo(busyboxImg.getId())); + } + + + @Test + public void commitNonExistingContainer() throws DockerException { + try { + dockerClient.commitCmd("non-existent").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + +} diff --git a/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java new file mode 100644 index 00000000..58c95486 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java @@ -0,0 +1,81 @@ +package com.github.dockerjava.core.command; + +import static ch.lambdaj.Lambda.selectUnique; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; +import java.util.List; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.ChangeLog; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class ContainerDiffCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void testContainerDiff() throws DockerException { + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("touch", "/test" ).exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(); + assertThat(exitCode, equalTo(0)); + + List filesystemDiff = dockerClient.containerDiffCmd(container.getId()).exec(); + LOG.info("Container DIFF: {}", filesystemDiff.toString()); + + assertThat(filesystemDiff.size(), equalTo(1)); + ChangeLog testChangeLog = selectUnique(filesystemDiff, + hasField("path", equalTo("/test"))); + + assertThat(testChangeLog, hasField("path", equalTo("/test"))); + assertThat(testChangeLog, hasField("kind", equalTo(1))); + } + + @Test + public void testContainerDiffWithNonExistingContainer() throws DockerException { + try { + dockerClient.containerDiffCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + + +} diff --git a/src/test/java/com/github/dockerjava/core/command/CopyFileFromContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/CopyFileFromContainerCmdImplTest.java new file mode 100644 index 00000000..193b787c --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/CopyFileFromContainerCmdImplTest.java @@ -0,0 +1,70 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; + +import org.testng.ITestResult; +import org.testng.annotations.*; + +import java.io.InputStream; +import java.lang.reflect.Method; + +import static org.hamcrest.Matchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +@Test(groups = "integration") +public class CopyFileFromContainerCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void copyFromContainer() throws Exception { + // TODO extract this into a shared method + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withName("docker-java-itest-copyFromContainer") + .withCmd("touch", "/copyFromContainer") + .exec(); + + LOG.info("Created container: {}", container); + assertThat(container.getId(), not(isEmptyOrNullString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InputStream response = dockerClient.copyFileFromContainerCmd(container.getId(), "/copyFromContainer").exec(); + boolean bytesAvailable = response.available() > 0; + assertTrue(bytesAvailable, "The file was not copied from the container."); + + // read the stream fully. Otherwise, the underlying stream will not be closed. + String responseAsString = asString(response); + assertNotNull(responseAsString); + assertTrue(responseAsString.length() > 0); + } + + @Test + public void copyFromNonExistingContainer() throws Exception { + try { + dockerClient.copyFileFromContainerCmd("non-existing", "/test").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException ignored) { + } + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java new file mode 100644 index 00000000..c652f51d --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java @@ -0,0 +1,568 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.ConflictException; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.*; +import com.github.dockerjava.client.AbstractDockerClientTest; + +import org.testng.ITestResult; +import org.testng.annotations.*; + +import java.lang.reflect.Method; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.UUID; + +import static com.github.dockerjava.api.model.Capability.MKNOD; +import static com.github.dockerjava.api.model.Capability.NET_ADMIN; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +@Test(groups = "integration") +public class CreateContainerCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void createContainerWithExistingName() throws DockerException { + + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("env") + .withName(containerName).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + try { + dockerClient.createContainerCmd("busybox").withCmd("env") + .withName(containerName).exec(); + fail("expected ConflictException"); + } catch (ConflictException e) { + } + } + + @Test + public void createContainerWithVolume() throws DockerException { + + Volume volume = new Volume("/var/log"); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox") + .withVolumes(volume).withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + LOG.info("Inspect container {}", inspectContainerResponse.getConfig() + .getVolumes()); + + assertThat(inspectContainerResponse.getConfig().getVolumes().keySet(), + contains("/var/log")); + + assertThat(inspectContainerResponse.getVolumesRW(), + hasItemInArray(new VolumeRW(volume, AccessMode.rw))); + } + + @Test + public void createContainerWithReadOnlyVolume() throws DockerException { + + Volume volume = new Volume("/srv/test"); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox") + .withVolumes(volume) + .withCmd("true") + .exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + LOG.info("Inspect container {}", inspectContainerResponse.getConfig() + .getVolumes()); + + assertThat(inspectContainerResponse.getConfig().getVolumes().keySet(), + contains("/srv/test")); + + assertThat(Arrays.asList(inspectContainerResponse.getVolumesRW()), + contains(new VolumeRW(volume))); + } + + @Test + public void createContainerWithVolumesFrom() throws DockerException { + + Volume volume1 = new Volume("/opt/webapp1"); + Volume volume2 = new Volume("/opt/webapp2"); + + String container1Name = UUID.randomUUID().toString(); + + // create a running container with bind mounts + CreateContainerResponse container1 = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999") + .withName(container1Name) + .withBinds(new Bind("/src/webapp1", volume1), new Bind("/src/webapp2", volume2)) + .exec(); + LOG.info("Created container1 {}", container1.toString()); + + dockerClient.startContainerCmd(container1.getId()).exec(); + LOG.info("Started container1 {}", container1.toString()); + + InspectContainerResponse inspectContainerResponse1 = dockerClient.inspectContainerCmd( + container1.getId()).exec(); + + assertContainerHasVolumes(inspectContainerResponse1, volume1, volume2); + + // create a second container with volumes from first container + CreateContainerResponse container2 = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999") + .withVolumesFrom(new VolumesFrom(container1Name)).exec(); + LOG.info("Created container2 {}", container2.toString()); + + InspectContainerResponse inspectContainerResponse2 = dockerClient + .inspectContainerCmd(container2.getId()).exec(); + + // No volumes are created, the information is just stored in .HostConfig.VolumesFrom + assertThat(inspectContainerResponse2.getHostConfig().getVolumesFrom(), hasItemInArray(new VolumesFrom(container1Name))); + + // To ensure that the information stored in VolumesFrom really is considered + // when starting the container, we start it and verify that it has the same + // bind mounts as the first container. + // This is somehow out of scope here, but it helped me to understand how the + // VolumesFrom feature really works. + dockerClient.startContainerCmd(container2.getId()).exec(); + LOG.info("Started container2 {}", container2.toString()); + + inspectContainerResponse2 = dockerClient.inspectContainerCmd(container2.getId()).exec(); + + assertThat(inspectContainerResponse2.getHostConfig().getVolumesFrom(), hasItemInArray(new VolumesFrom(container1Name))); + assertContainerHasVolumes(inspectContainerResponse2, volume1, volume2); + } + + @Test + public void createContainerWithEnv() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withEnv("VARIABLE=success") + .withCmd("env").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat( + Arrays.asList(inspectContainerResponse.getConfig().getEnv()), + containsInAnyOrder("VARIABLE=success")); + + dockerClient.startContainerCmd(container.getId()).exec(); + + assertThat(asString(dockerClient.logContainerCmd(container.getId()) + .withStdOut().exec()), containsString("VARIABLE=success")); + } + + @Test + public void createContainerWithHostname() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withHostName("docker-java") + .withCmd("env").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getConfig().getHostName(), + equalTo("docker-java")); + + dockerClient.startContainerCmd(container.getId()).exec(); + + assertThat(asString(dockerClient.logContainerCmd(container.getId()) + .withStdOut().exec()), containsString("HOSTNAME=docker-java")); + } + + @Test + public void createContainerWithName() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withName("container") + .withCmd("env").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getName(), equalTo("/container")); + + try { + dockerClient.createContainerCmd("busybox").withName("container") + .withCmd("env").exec(); + fail("Expected ConflictException"); + } catch (ConflictException e) { + } + + } + + @Test + public void createContainerWithLink() throws DockerException { + + CreateContainerResponse container1 = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999").withName("container1").exec(); + LOG.info("Created container1 {}", container1.toString()); + assertThat(container1.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + InspectContainerResponse inspectContainerResponse1 = dockerClient + .inspectContainerCmd(container1.getId()).exec(); + LOG.info("Container1 Inspect: {}", inspectContainerResponse1.toString()); + assertThat(inspectContainerResponse1.getState().isRunning(), is(true)); + + HostConfig hostConfig = new HostConfig(); + hostConfig.setLinks(new Link("container1", "container1Link")); + CreateContainerResponse container2 = dockerClient + .createContainerCmd("busybox").withName("container2").withHostConfig(hostConfig) + .withCmd("env").exec(); + LOG.info("Created container {}", container2.toString()); + assertThat(container2.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse2 = dockerClient + .inspectContainerCmd(container2.getId()).exec(); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), equalTo(new Link[] {new Link("container1","container1Link")})); + } + + @Test + public void createContainerWithCapAddAndCapDrop() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox") + .withCapAdd(NET_ADMIN) + .withCapDrop(MKNOD).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig() + .getCapAdd()), contains(NET_ADMIN)); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig() + .getCapDrop()), contains(MKNOD)); + } + + @Test + public void createContainerWithDns() throws DockerException { + + String aDnsServer = "8.8.8.8"; + String anotherDnsServer = "8.8.4.4"; + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox") + .withCmd("true").withDns(aDnsServer, anotherDnsServer).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getDns()), + contains(aDnsServer, anotherDnsServer)); + } + + @Test + public void createContainerWithEntrypoint() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withName("container") + .withEntrypoint("sleep", "9999").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getConfig().getEntrypoint()), contains("sleep", "9999")); + + } + + @Test + public void createContainerWithExtraHosts() throws DockerException { + + String[] extraHosts = {"dockerhost:127.0.0.1", "otherhost:10.0.0.1"}; + + HostConfig hostConfig = new HostConfig(); + hostConfig.setExtraHosts(extraHosts); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withName("container") + .withHostConfig(hostConfig).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getExtraHosts()), + containsInAnyOrder("dockerhost:127.0.0.1", "otherhost:10.0.0.1")); + } + + @Test + public void createContainerWithDevices() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999") + .withDevices(new Device("rwm", "/dev/nulo", "/dev/zero")) + .exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig() + .getDevices()), contains(new Device("rwm", "/dev/nulo", + "/dev/zero"))); + } + + @Test + public void createContainerWithPortBindings() throws DockerException { + + ExposedPort tcp22 = ExposedPort.tcp(22); + ExposedPort tcp23 = ExposedPort.tcp(23); + + Ports portBindings = new Ports(); + portBindings.bind(tcp22, Ports.Binding(11022)); + portBindings.bind(tcp23, Ports.Binding(11023)); + portBindings.bind(tcp23, Ports.Binding(11024)); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("true") + .withExposedPorts(tcp22, tcp23) + .withPortBindings(portBindings) + .exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getConfig() + .getExposedPorts()), contains(tcp22, tcp23)); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings() + .getBindings().get(tcp22)[0], is(equalTo(Ports.Binding(11022)))); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings() + .getBindings().get(tcp23)[0], is(equalTo(Ports.Binding(11023)))); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings() + .getBindings().get(tcp23)[1], is(equalTo(Ports.Binding(11024)))); + + } + + @Test + public void createContainerWithLinking() throws DockerException { + + CreateContainerResponse container1 = dockerClient + .createContainerCmd("busybox") + .withCmd("sleep", "9999") + .withName("container1").exec(); + + LOG.info("Created container1 {}", container1.toString()); + assertThat(container1.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + InspectContainerResponse inspectContainerResponse1 = dockerClient + .inspectContainerCmd(container1.getId()).exec(); + LOG.info("Container1 Inspect: {}", inspectContainerResponse1.toString()); + + assertThat(inspectContainerResponse1.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse1.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getId(), + startsWith(container1.getId())); + assertThat(inspectContainerResponse1.getName(), equalTo("/container1")); + assertThat(inspectContainerResponse1.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getState(), is(notNullValue())); + assertThat(inspectContainerResponse1.getState().isRunning(), is(true)); + + if (!inspectContainerResponse1.getState().isRunning()) { + assertThat(inspectContainerResponse1.getState().getExitCode(), + is(equalTo(0))); + } + + CreateContainerResponse container2 = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container2") + .withLinks(new Link("container1", "container1Link")) + .exec(); + + LOG.info("Created container2 {}", container2.toString()); + assertThat(container2.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse2 = dockerClient + .inspectContainerCmd(container2.getId()).exec(); + LOG.info("Container2 Inspect: {}", inspectContainerResponse2.toString()); + + assertThat(inspectContainerResponse2.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse2.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse2.getHostConfig(), + is(notNullValue())); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), + is(notNullValue())); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), equalTo(new Link[] { new Link("container1", + "container1Link") })); + assertThat(inspectContainerResponse2.getId(), + startsWith(container2.getId())); + assertThat(inspectContainerResponse2.getName(), equalTo("/container2")); + assertThat(inspectContainerResponse2.getImageId(), not(isEmptyString())); + + + } + + @Test + public void createContainerWithRestartPolicy() throws DockerException { + + RestartPolicy restartPolicy = RestartPolicy.onFailureRestart(5); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox") + .withCmd("sleep", "9999") + .withRestartPolicy(restartPolicy) + .exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getHostConfig().getRestartPolicy(), + is(equalTo(restartPolicy))); + } + + /** + * This tests support for --net option for the docker run command: + * --net="bridge" Set the Network mode for the container 'bridge': creates a + * new network stack for the container on the docker bridge 'none': no + * networking for this container 'container:': reuses another container + * network stack 'host': use the host network stack inside the container. + * Note: the host mode gives the container full access to local system + * services such as D-bus and is therefore considered insecure. + */ + @Test + public void createContainerWithNetworkMode() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox") + .withCmd("true") + .withNetworkMode("host") + .exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getHostConfig().getNetworkMode(), + is(equalTo("host"))); + } + + @Test + public void createContainerWithMacAddress() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox") + .withMacAddress("00:80:41:ae:fd:7e") + .withCmd("true") + .exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertEquals(inspectContainerResponse.getConfig().getMacAddress(), + "00:80:41:ae:fd:7e"); + } + + @Test(groups = "ignoreInCircleCi") + public void createContainerWithULimits() throws DockerException { + + Ulimit[] ulimits = {new Ulimit("nproc", 709, 1026), new Ulimit("nofile", 1024, 4096)}; + + HostConfig hostConfig = new HostConfig(); + hostConfig.setUlimits(ulimits); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withName("container") + .withHostConfig(hostConfig).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig().getUlimits()), + containsInAnyOrder(new Ulimit("nproc", 709, 1026), new Ulimit("nofile", 1024, 4096))); + + } + +} diff --git a/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java b/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java new file mode 100644 index 00000000..53131c61 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/DockerfileFixture.java @@ -0,0 +1,102 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.InternalServerErrorException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.model.Image; +import com.github.dockerjava.client.AbstractDockerClientTest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * Start and stop a single container for testing. + */ +public class DockerfileFixture implements AutoCloseable { + + private static final Logger LOGGER = LoggerFactory.getLogger(DockerfileFixture.class); + private final DockerClient dockerClient; + private String directory; + private String repository; + private String containerId; + + public DockerfileFixture(DockerClient dockerClient, String directory) { + this.dockerClient = dockerClient; + this.directory = directory; + } + + public void open() throws IOException { + + LOGGER.info("building {}", directory); + InputStream response = dockerClient + .buildImageCmd(new File("src/test/resources", directory)) + .withNoCache() // remove alternatives, cause problems + .exec(); + + String log = AbstractDockerClientTest.consumeAsString(response); + + assertThat(log, containsString("Successfully built")); + + Image lastCreatedImage = dockerClient + .listImagesCmd() + .exec() + .get(0); + + repository = lastCreatedImage + .getRepoTags()[0]; + + LOGGER.info("created {} {}", lastCreatedImage.getId(), repository); + + containerId = dockerClient + .createContainerCmd(lastCreatedImage.getId()) + .exec() + .getId(); + + LOGGER.info("starting {}", containerId); + + dockerClient + .startContainerCmd(containerId) + .exec(); + } + + @Override + public void close() throws Exception { + + if (containerId != null) { + LOGGER.info("removing container {}", containerId); + try { + dockerClient + .removeContainerCmd(containerId) + .withForce() // stop too + .exec(); + } catch (NotFoundException | InternalServerErrorException ignored) { + LOGGER.info("ignoring {}", ignored.getMessage()); + } + containerId = null; + } + + if (repository != null) { + LOGGER.info("removing repository {}", repository); + try { + dockerClient + .removeImageCmd(repository) + .withForce() + .exec(); + } catch (NotFoundException | InternalServerErrorException e) { + LOGGER.info("ignoring {}", e.getMessage()); + } + repository = null; + } + } + + public String getContainerId() { + return containerId; + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/EventStreamReaderITest.java b/src/test/java/com/github/dockerjava/core/command/EventStreamReaderITest.java new file mode 100644 index 00000000..07bca25b --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/EventStreamReaderITest.java @@ -0,0 +1,72 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.EventStreamItem; +import com.github.dockerjava.api.model.PullEventStreamItem; +import com.github.dockerjava.core.DockerClientBuilder; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.File; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.beans.HasPropertyWithValue.hasProperty; +import static org.hamcrest.core.AllOf.allOf; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsNull.nullValue; +import static org.testng.AssertJUnit.assertNull; + + +@Test(groups = "integration") +public class EventStreamReaderITest { + + private DockerClient dockerClient; + + @BeforeTest + public void setUp() throws Exception { + dockerClient = DockerClientBuilder.getInstance().build(); + } + + @AfterTest + public void tearDown() throws Exception { + dockerClient.close(); + } + + @Test(groups = "ignoreInCircleCi") + public void pullCanBeStreamed() throws Exception { + + try (EventStreamReader reader = new EventStreamReader<>( + dockerClient.pullImageCmd("busybox:latest").exec(), + PullEventStreamItem.class) + ) {; + assertThat(reader.readItem(), + allOf( + hasProperty("status", equalTo("Pulling from busybox")), + hasProperty("progress", nullValue()), + hasProperty("progressDetail", nullValue()) + ) + ); + assertNull(reader.readItem()); + } + } + + @Test + public void buildCanBeStreamed() throws Exception { + + try (EventStreamReader reader = new EventStreamReader<>( + dockerClient.buildImageCmd(new File("src/test/resources/eventStreamReaderDockerfile")).exec(), + EventStreamItem.class) + ) { + assertThat(reader.readItem(), + allOf( + hasProperty("stream", equalTo("Step 0 : FROM busybox:latest\n")), + hasProperty("error", nullValue()), + hasProperty("errorDetail", nullValue()) + ) + ); + assertNull(reader.readItem()); + + } + } +} \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/core/command/EventsCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/EventsCmdImplTest.java new file mode 100644 index 00000000..569283ce --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/EventsCmdImplTest.java @@ -0,0 +1,172 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.EventCallback; +import com.github.dockerjava.api.command.EventsCmd; +import com.github.dockerjava.api.model.Event; +import com.github.dockerjava.client.AbstractDockerClientTest; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +@Test(groups = "integration") +public class EventsCmdImplTest extends AbstractDockerClientTest { + + private static int KNOWN_NUM_EVENTS = 4; + + private static String getEpochTime() { + return String.valueOf(System.currentTimeMillis() / 1000); + } + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void testEventStreamTimeBound() throws InterruptedException, + IOException { + // Don't include other tests events + TimeUnit.SECONDS.sleep(1); + + String startTime = getEpochTime(); + int expectedEvents = generateEvents(); + String endTime = getEpochTime(); + + CountDownLatch countDownLatch = new CountDownLatch(expectedEvents); + EventCallbackTest eventCallback = new EventCallbackTest(countDownLatch); + + EventsCmd eventsCmd = dockerClient.eventsCmd(eventCallback) + .withSince(startTime).withUntil(endTime); + ExecutorService executorService = eventsCmd.exec(); + + boolean zeroCount = countDownLatch.await(5, TimeUnit.SECONDS); + + executorService.shutdown(); + eventCallback.close(); + + assertTrue(zeroCount, "" + eventCallback.getEvents()); + } + + @Test + public void testEventStreaming1() throws InterruptedException, IOException { + // Don't include other tests events + TimeUnit.SECONDS.sleep(1); + + CountDownLatch countDownLatch = new CountDownLatch(KNOWN_NUM_EVENTS); + EventCallbackTest eventCallback = new EventCallbackTest(countDownLatch); + + EventsCmd eventsCmd = dockerClient.eventsCmd(eventCallback).withSince( + getEpochTime()); + ExecutorService executorService = eventsCmd.exec(); + + generateEvents(); + + boolean zeroCount = countDownLatch.await(5, TimeUnit.SECONDS); + executorService.shutdown(); + eventCallback.close(); + assertTrue(zeroCount, "Expected 4 events, [create, start, die, stop]"); + } + + @Test + public void testEventStreaming2() throws InterruptedException, IOException { + // Don't include other tests events + TimeUnit.SECONDS.sleep(1); + + CountDownLatch countDownLatch = new CountDownLatch(KNOWN_NUM_EVENTS); + EventCallbackTest eventCallback = new EventCallbackTest(countDownLatch); + + EventsCmd eventsCmd = dockerClient.eventsCmd(eventCallback).withSince( + getEpochTime()); + ExecutorService executorService = eventsCmd.exec(); + + generateEvents(); + + boolean zeroCount = countDownLatch.await(5, TimeUnit.SECONDS); + executorService.shutdown(); + eventCallback.close(); + assertTrue(zeroCount, "Expected 4 events, [create, start, die, stop]"); + } + + /** + * This method generates {#link KNOWN_NUM_EVENTS} events + */ + private int generateEvents() { + String testImage = "busybox"; + asString(dockerClient.pullImageCmd(testImage).exec()); + CreateContainerResponse container = dockerClient + .createContainerCmd(testImage).withCmd("sleep", "9999").exec(); + dockerClient.startContainerCmd(container.getId()).exec(); + dockerClient.stopContainerCmd(container.getId()).exec(); + return KNOWN_NUM_EVENTS; + } + + private class EventCallbackTest implements EventCallback { + private final CountDownLatch countDownLatch; + private final AtomicBoolean isReceiving = new AtomicBoolean(true); + private final List events = new ArrayList(); + + public EventCallbackTest(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + public void close() { + isReceiving.set(false); + } + + @Override + public void onEvent(Event event) { + LOG.info("Received event #{}: {}", countDownLatch.getCount(), event); + countDownLatch.countDown(); + events.add(event); + } + + @Override + public void onException(Throwable throwable) { + LOG.error("Error occurred: {}", throwable.getMessage()); + } + + @Override + public void onCompletion(int numEvents) { + LOG.info("Number of events received: {}", numEvents); + } + + @Override + public boolean isReceiving() { + return isReceiving.get(); + } + + public List getEvents() { + return new ArrayList(events); + } + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/ExecCreateCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ExecCreateCmdImplTest.java new file mode 100644 index 00000000..028f4cac --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/ExecCreateCmdImplTest.java @@ -0,0 +1,57 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; +import org.testng.ITestResult; +import org.testng.annotations.*; + +import java.lang.reflect.Method; +import java.security.SecureRandom; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +@Test(groups = "integration") +public class ExecCreateCmdImplTest extends AbstractDockerClientTest { + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void execCreateTest() { + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("top") + .withName(containerName).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()).withCmd("touch","file.log").exec(); + + assertThat(execCreateCmdResponse.getId(), not(isEmptyString())); + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java new file mode 100644 index 00000000..80619efe --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java @@ -0,0 +1,67 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; +import org.testng.ITestResult; +import org.testng.annotations.*; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.security.SecureRandom; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +@Test(groups = "integration") +public class ExecStartCmdImplTest extends AbstractDockerClientTest { + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void execStartTest() throws Exception { + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox") + .withCmd("top") + .withName(containerName).exec(); + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) + .withAttachStdout(true) + .withCmd("touch", "/execStartTest.log").exec(); + dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(); + + InputStream response = dockerClient.copyFileFromContainerCmd(container.getId(), "/execStartTest.log").exec(); + boolean bytesAvailable = response.available() > 0; + assertTrue(bytesAvailable, "The file was not copied from the container."); + + // read the stream fully. Otherwise, the underlying stream will not be closed. + String responseAsString = asString(response); + assertNotNull(responseAsString); + assertTrue(responseAsString.length() > 0); + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java b/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java new file mode 100644 index 00000000..24c83355 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/FrameReaderITest.java @@ -0,0 +1,97 @@ +package com.github.dockerjava.core.command; + +import static org.testng.Assert.assertEquals; +import static org.testng.AssertJUnit.assertNull; + +import java.io.IOException; +import java.io.InputStream; + +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.StreamType; +import com.github.dockerjava.core.DockerClientBuilder; + +@Test(groups = "integration") +public class FrameReaderITest { + + private DockerClient dockerClient; + private DockerfileFixture dockerfileFixture; + + @BeforeTest + public void beforeTest() throws Exception { + dockerClient = DockerClientBuilder.getInstance().build(); + dockerfileFixture = new DockerfileFixture(dockerClient, + "frameReaderDockerfile"); + dockerfileFixture.open(); + } + + @AfterTest + public void deleteDockerContainerImage() throws Exception { + dockerfileFixture.close(); + dockerClient.close(); + } + + @Test + public void canCloseFrameReaderAndReadExpectedLines() throws Exception { + + // wait for the container to be successfully executed + int exitCode = dockerClient.waitContainerCmd( + dockerfileFixture.getContainerId()).exec(); + assertEquals(0, exitCode); + + InputStream response = getLoggerStream(); + + try (FrameReader reader = new FrameReader(response)) { + assertEquals(reader.readFrame(), new Frame(StreamType.STDOUT, + "to stdout\n".getBytes())); + assertEquals(reader.readFrame(), new Frame(StreamType.STDERR, + "to stderr\n".getBytes())); + assertNull(reader.readFrame()); + } + } + + private InputStream getLoggerStream() { + + return dockerClient.logContainerCmd(dockerfileFixture.getContainerId()) + .withStdOut() + .withStdErr() + .withTailAll() + // we can't follow stream here as it blocks reading from resulting InputStream infinitely + //.withFollowStream() + .exec(); + } + + @Test + public void canLogInOneThreadAndExecuteCommandsInAnother() throws Exception { + + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + try (FrameReader reader = new FrameReader(getLoggerStream())) { + // noinspection StatementWithEmptyBody + while (reader.readFrame() != null) { + // nop + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + + thread.start(); + + try (DockerfileFixture busyboxDockerfile = new DockerfileFixture( + dockerClient, "busyboxDockerfile")) { + busyboxDockerfile.open(); + } + + thread.join(); + + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/FrameReaderTest.java b/src/test/java/com/github/dockerjava/core/command/FrameReaderTest.java new file mode 100644 index 00000000..2b5e2b64 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/FrameReaderTest.java @@ -0,0 +1,57 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.StreamType; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +public class FrameReaderTest { + public static final int HEADER_SIZE = 8; + private final List bytes = new ArrayList<>(); + private final InputStream inputStream = new InputStream() { + @Override + public int read() throws IOException { + return bytes.isEmpty() ? -1 : bytes.remove(0); + } + }; + private final FrameReader frameReader = new FrameReader(inputStream); + + @Test + public void endOfStreamReturnsNull() throws Exception { + assertNull(nextFrame()); + } + + @Test + public void stdInBytesFrameReturnsFrame() throws Exception { + assertEquals(nextFrame(0, 0, 0, 0, 0, 0, 0, 0), new Frame(StreamType.STDIN, new byte[0])); + } + + private Frame nextFrame(int... bytes) throws IOException { + setBytes(bytes); + return frameReader.readFrame(); + } + + @Test + public void stdOutBytesFrameReturnsFrame() throws Exception { + assertEquals(nextFrame(1, 0, 0, 0, 0, 0, 0, 0), new Frame(StreamType.STDOUT, new byte[0])); + } + + @Test + public void stdErrBytesFrameReturnsFrame() throws Exception { + assertEquals(nextFrame(2, 0, 0, 0, 0, 0, 0, 0), new Frame(StreamType.STDERR, new byte[0])); + } + + private void setBytes(int... bytes) { + this.bytes.clear(); + for (int aByte : bytes) { + this.bytes.add(aByte); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/core/command/InfoCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/InfoCmdImplTest.java new file mode 100644 index 00000000..7170c12f --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/InfoCmdImplTest.java @@ -0,0 +1,73 @@ +package com.github.dockerjava.core.command; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.client.AbstractDockerClientTest; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; + +@Test(groups = "integration") +public class InfoCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void info() throws DockerException { + // Make sure that there is at least one container for the assertion + // TODO extract this into a shared method + if (dockerClient.listContainersCmd().withShowAll(true).exec().size() == 0) { + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withName("docker-java-itest-info") + .withCmd("touch", "/test") + .exec(); + + LOG.info("Created container: {}", container); + assertThat(container.getId(), not(isEmptyOrNullString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + } + + Info dockerInfo = dockerClient.infoCmd().exec(); + LOG.info(dockerInfo.toString()); + + assertTrue(dockerInfo.toString().contains("containers")); + assertTrue(dockerInfo.toString().contains("images")); + assertTrue(dockerInfo.toString().contains("debug")); + + assertTrue(dockerInfo.getContainers() > 0); + assertTrue(dockerInfo.getImages() > 0); + assertTrue(dockerInfo.getNFd() > 0); + assertTrue(dockerInfo.getNGoroutines() > 0); + assertTrue(dockerInfo.getNCPU() > 0); + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java new file mode 100644 index 00000000..f1270f4e --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java @@ -0,0 +1,104 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.InspectExecResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; +import com.github.dockerjava.test.serdes.JSONTestHelper; +import java.io.IOException; + +import org.testng.ITestResult; +import org.testng.annotations.*; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.security.SecureRandom; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +@Test(groups = "integration") +public class InspectExecCmdImplTest extends AbstractDockerClientTest { + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void inspectExecTest() throws IOException { + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox") + .withCmd("top") + .withName(containerName).exec(); + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + ExecCreateCmdResponse touchFileCmdCreateResponse = dockerClient.execCreateCmd(container.getId()) + .withAttachStdout() + .withAttachStderr() + .withCmd("touch", "/marker").exec(); + LOG.info("Created exec {}", touchFileCmdCreateResponse.toString()); + assertThat(touchFileCmdCreateResponse.getId(), not(isEmptyString())); + ExecCreateCmdResponse checkFileCmdCreateResponse = dockerClient.execCreateCmd(container.getId()) + .withAttachStdout() + .withAttachStderr() + .withCmd("test", "-e", "/marker").exec(); + LOG.info("Created exec {}", checkFileCmdCreateResponse.toString()); + assertThat(checkFileCmdCreateResponse.getId(), not(isEmptyString())); + + // Check that file does not exist + InputStream response1 = dockerClient.execStartCmd(container.getId()) + .withExecId(checkFileCmdCreateResponse.getId()) + .exec(); + asString(response1); // consume + + InspectExecResponse first = dockerClient.inspectExecCmd(checkFileCmdCreateResponse.getId()).exec(); + assertThat(first.getExitCode(), is(1)); + + // Create the file + InputStream response2 = dockerClient.execStartCmd(container.getId()) + .withExecId(touchFileCmdCreateResponse.getId()) + .exec(); + asString(response2); + + InspectExecResponse second = dockerClient.inspectExecCmd(touchFileCmdCreateResponse.getId()).exec(); + assertThat(second.getExitCode(), is(0)); + + // Check that file does exist now + InputStream response3 = dockerClient.execStartCmd(container.getId()) + .withExecId(checkFileCmdCreateResponse.getId()) + .exec(); + asString(response3); + + InspectExecResponse third = dockerClient.inspectExecCmd(checkFileCmdCreateResponse.getId()).exec(); + assertThat(third.getExitCode(), is(0)); + + // Get container info and check its roundtrip to ensure the consistency + InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(container.getId()).exec(); + assertEquals(containerInfo.getId(), container.getId()); + JSONTestHelper.testRoundTrip(containerInfo); + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/KillContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/KillContainerCmdImplTest.java new file mode 100644 index 00000000..33d80ef2 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/KillContainerCmdImplTest.java @@ -0,0 +1,85 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class KillContainerCmdImplTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory + .getLogger(KillContainerCmdImplTest.class); + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void killContainer() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + LOG.info("Killing container: {}", container.getId()); + dockerClient.killContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + assertThat(inspectContainerResponse.getState().isRunning(), + is(equalTo(false))); + assertThat(inspectContainerResponse.getState().getExitCode(), + not(equalTo(0))); + + } + + @Test + public void killNonExistingContainer() throws DockerException { + + try { + dockerClient.killContainerCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + +} diff --git a/src/test/java/com/github/dockerjava/core/command/ListContainersCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ListContainersCmdImplTest.java new file mode 100644 index 00000000..f73ac5e4 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/ListContainersCmdImplTest.java @@ -0,0 +1,106 @@ +package com.github.dockerjava.core.command; + +import static ch.lambdaj.Lambda.filter; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; +import java.util.List; + +import org.hamcrest.Matcher; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class ListContainersCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void testListContainers() throws DockerException { + + String testImage = "busybox"; + + // need to block until image is pulled completely + asString(dockerClient.pullImageCmd(testImage).exec()); + + List containers = dockerClient.listContainersCmd().withShowAll(true).exec(); + assertThat(containers, notNullValue()); + LOG.info("Container List: {}", containers); + + int size = containers.size(); + + CreateContainerResponse container1 = dockerClient + .createContainerCmd(testImage).withCmd("echo").exec(); + + assertThat(container1.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container1.getId()).exec(); + + assertThat(inspectContainerResponse.getConfig().getImage(), is(equalTo(testImage))); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + LOG.info("container id: " + container1.getId()); + + List containers2 = dockerClient.listContainersCmd().withShowAll(true).exec(); + + for(Container container: containers2) { + LOG.info("listContainer: id=" + container.getId() +" image=" + container.getImage()); + } + + assertThat(size + 1, is(equalTo(containers2.size()))); + Matcher matcher = hasItem(hasField("id", startsWith(container1.getId()))); + assertThat(containers2, matcher); + + List filteredContainers = filter( + hasField("id", startsWith(container1.getId())), containers2); + assertThat(filteredContainers.size(), is(equalTo(1))); + + for(Container container: filteredContainers) { + LOG.info("filteredContainer: " + container); + } + + Container container2 = filteredContainers.get(0); + assertThat(container2.getCommand(), not(isEmptyString())); + assertThat(container2.getImage(), startsWith(testImage + ":")); + } + + + +} diff --git a/src/test/java/com/github/dockerjava/core/command/ListImagesCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ListImagesCmdImplTest.java new file mode 100644 index 00000000..05bf9483 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/ListImagesCmdImplTest.java @@ -0,0 +1,99 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +import java.lang.reflect.Method; +import java.util.List; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.model.Image; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class ListImagesCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void listImages() throws DockerException { + List images = dockerClient.listImagesCmd().withShowAll(true).exec(); + assertThat(images, notNullValue()); + LOG.info("Images List: {}", images); + Info info = dockerClient.infoCmd().exec(); + + assertThat(images.size(), equalTo(info.getImages())); + + Image img = images.get(0); + assertThat(img.getCreated(), is(greaterThan(0L))); + assertThat(img.getVirtualSize(), is(greaterThan(0L))); + assertThat(img.getId(), not(isEmptyString())); + assertThat(img.getRepoTags(), not(emptyArray())); + } + + @Test(groups = "ignoreInCircleCi") + public void listDanglingImages() throws DockerException { + String imageId = createDanglingImage(); + List images = dockerClient.listImagesCmd() + .withFilters("{\"dangling\":[\"true\"]}") + .withShowAll(true).exec(); + assertThat(images, notNullValue()); + LOG.info("Images List: {}", images); + assertThat(images.size(), is(greaterThan(0))); + boolean imageInFilteredList = isImageInFilteredList(images, imageId); + assertTrue(imageInFilteredList); + } + + private boolean isImageInFilteredList(List images, String expectedImageId) { + for (Image image : images) { + if (expectedImageId.equals(image.getId())) { + return true; + } + } + return false; + } + + private String createDanglingImage() { + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "5").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + LOG.info("Committing container {}", container.toString()); + String imageId = dockerClient + .commitCmd(container.getId()).exec(); + + dockerClient.stopContainerCmd(container.getId()).exec(); + dockerClient.killContainerCmd(container.getId()).exec(); + dockerClient.removeContainerCmd(container.getId()).exec(); + return imageId; + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java new file mode 100644 index 00000000..94a27b1d --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java @@ -0,0 +1,119 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.io.InputStream; +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class LogContainerCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void logContainer() throws Exception { + + String snippet = "hello world"; + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("/bin/echo", snippet).exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(); + + assertThat(exitCode, equalTo(0)); + + InputStream response = dockerClient.logContainerCmd(container.getId()).withStdErr().withStdOut().exec(); + + String log = asString(response); + + //LOG.info("resonse: " + log); + + assertThat(log, endsWith(snippet)); + } + + @Test + public void logNonExistingContainer() throws Exception { + + try { + dockerClient.logContainerCmd("non-existing").withStdErr().withStdOut().exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + + @Test + public void multipleLogContainer() throws Exception { + + String snippet = "hello world"; + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("/bin/echo", snippet).exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(); + + assertThat(exitCode, equalTo(0)); + + InputStream response = dockerClient.logContainerCmd(container.getId()).withStdErr().withStdOut().exec(); + + response.close(); + + //String log = asString(response); + + response = dockerClient.logContainerCmd(container.getId()).withStdErr().withStdOut().exec(); + + //log = asString(response); + response.close(); + + response = dockerClient.logContainerCmd(container.getId()).withStdErr().withStdOut().exec(); + + String log = asString(response); + + assertThat(log, endsWith(snippet)); + } + + +} diff --git a/src/test/java/com/github/dockerjava/core/command/PullImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/PullImageCmdImplTest.java new file mode 100644 index 00000000..f9968a44 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/PullImageCmdImplTest.java @@ -0,0 +1,117 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.InternalServerErrorException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.InspectImageResponse; +import com.github.dockerjava.api.command.PullImageCmd; +import com.github.dockerjava.api.model.Info; +import com.github.dockerjava.client.AbstractDockerClientTest; +import org.testng.ITestResult; +import org.testng.annotations.*; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +@Test(groups = "integration") +public class PullImageCmdImplTest extends AbstractDockerClientTest { + + private static final PullImageCmd.Exec NOP_EXEC = new PullImageCmd.Exec() { + @Override + public InputStream exec(PullImageCmd command) { + return null; + } + }; + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void nullAuthConfig() throws Exception { + PullImageCmdImpl pullImageCmd = new PullImageCmdImpl(NOP_EXEC, null, ""); + try { + pullImageCmd.withAuthConfig(null); + fail(); + } catch (Exception e) { + assertEquals(e.getMessage(), "authConfig was not specified"); + } + } + + @Test + public void testPullImage() throws DockerException, IOException { + Info info = dockerClient.infoCmd().exec(); + LOG.info("Client info: {}", info.toString()); + + int imgCount = info.getImages(); + LOG.info("imgCount1: {}", imgCount); + + // This should be an image that is not used by other repositories + // already + // pulled down, preferably small in size. If tag is not used pull will + // download all images in that repository but tmpImgs will only + // deleted 'latest' image but not images with other tags + String testImage = "hackmann/empty"; + + LOG.info("Removing image: {}", testImage); + + try { + dockerClient.removeImageCmd(testImage).withForce().exec(); + } catch (NotFoundException e) { + // just ignore if not exist + } + + + info = dockerClient.infoCmd().exec(); + LOG.info("Client info: {}", info.toString()); + + imgCount = info.getImages(); + LOG.info("imgCount2: {}", imgCount); + + LOG.info("Pulling image: {}", testImage); + + InputStream response = dockerClient.pullImageCmd(testImage).exec(); + + assertThat(asString(response), containsString("Download complete")); + + info = dockerClient.infoCmd().exec(); + LOG.info("Client info after pull, {}", info.toString()); + + assertThat(imgCount, lessThanOrEqualTo(info.getImages())); + + InspectImageResponse inspectImageResponse = dockerClient + .inspectImageCmd(testImage).exec(); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + assertThat(inspectImageResponse, notNullValue()); + } + + @Test + public void testPullNonExistingImage() throws DockerException, IOException { + + // does not throw an exception + InputStream is = dockerClient.pullImageCmd("xvxcv/foo").exec(); + // stream needs to be fully read in order to close the underlying connection + asString(is); + } + +} diff --git a/src/test/java/com/github/dockerjava/core/command/PushImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/PushImageCmdImplTest.java new file mode 100644 index 00000000..f409aebf --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/PushImageCmdImplTest.java @@ -0,0 +1,75 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.*; + +import java.lang.reflect.Method; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +@Test(groups = "integration") +public class PushImageCmdImplTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory + .getLogger(PushImageCmdImplTest.class); + + String username; + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + username = dockerClient.authConfig().getUsername(); + } + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void pushLatest() throws Exception { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + LOG.info("Committing container: {}", container.toString()); + String imageId = dockerClient.commitCmd(container.getId()).withRepository(username + "/busybox").exec(); + + // we have to block until image is pushed + asString(dockerClient.pushImageCmd(username + "/busybox").exec()); + + LOG.info("Removing image: {}", imageId); + dockerClient.removeImageCmd(imageId).exec(); + + String response = asString(dockerClient.pullImageCmd(username + "/busybox").exec()); + + assertThat(response, not(containsString("HTTP code: 404"))); + } + + @Test + public void pushExistentImage() throws Exception { + + assertThat(asString(dockerClient.pushImageCmd(username + "/xxx").exec()), containsString("error")); + } + +} + diff --git a/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java new file mode 100644 index 00000000..b6fe173a --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java @@ -0,0 +1,84 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; +import java.util.List; + +import org.hamcrest.Matcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class RemoveContainerCmdImplTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory + .getLogger(RemoveContainerCmdImplTest.class); + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void removeContainer() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("true").exec(); + + dockerClient.startContainerCmd(container.getId()).exec(); + dockerClient.waitContainerCmd(container.getId()).exec(); + + LOG.info("Removing container: {}", container.getId()); + dockerClient.removeContainerCmd(container.getId()).exec(); + + List containers2 = dockerClient.listContainersCmd().withShowAll(true).exec(); + + Matcher matcher = not(hasItem(hasField("id", + startsWith(container.getId())))); + assertThat(containers2, matcher); + + } + + @Test + public void removeNonExistingContainer() throws DockerException { + try { + dockerClient.removeContainerCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + + +} + diff --git a/src/test/java/com/github/dockerjava/core/command/RemoveImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/RemoveImageCmdImplTest.java new file mode 100644 index 00000000..af6bc39c --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/RemoveImageCmdImplTest.java @@ -0,0 +1,92 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; +import java.util.List; + +import org.hamcrest.Matcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class RemoveImageCmdImplTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory + .getLogger(RemoveImageCmdImplTest.class); + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void removeImage() throws DockerException, InterruptedException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + LOG.info("Committing container {}", container.toString()); + String imageId = dockerClient + .commitCmd(container.getId()).exec(); + + dockerClient.stopContainerCmd(container.getId()).exec(); + dockerClient.killContainerCmd(container.getId()).exec(); + dockerClient.removeContainerCmd(container.getId()).exec(); + + LOG.info("Removing image: {}", imageId); + dockerClient.removeImageCmd(imageId).exec(); + + List containers = dockerClient.listContainersCmd().withShowAll(true).exec(); + + Matcher matcher = not(hasItem(hasField("id", startsWith(imageId)))); + assertThat(containers, matcher); + } + + @Test + public void removeNonExistingImage() throws DockerException, InterruptedException { + try { + dockerClient.removeImageCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + + } + + +} + diff --git a/src/test/java/com/github/dockerjava/core/command/RestartContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/RestartContainerCmdImplTest.java new file mode 100644 index 00000000..b6040a0f --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/RestartContainerCmdImplTest.java @@ -0,0 +1,90 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class RestartContainerCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void restartContainer() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + String startTime = inspectContainerResponse.getState().getStartedAt(); + + dockerClient.restartContainerCmd(container.getId()).withtTimeout(2).exec(); + + InspectContainerResponse inspectContainerResponse2 = dockerClient + .inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect After Restart: {}", + inspectContainerResponse2.toString()); + + String startTime2 = inspectContainerResponse2.getState().getStartedAt(); + + assertThat(startTime, not(equalTo(startTime2))); + + assertThat(inspectContainerResponse.getState().isRunning(), + is(equalTo(true))); + + dockerClient.killContainerCmd(container.getId()).exec(); + } + + @Test + public void restartNonExistingContainer() throws DockerException, InterruptedException { + try { + dockerClient.restartContainerCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + + } + + +} diff --git a/src/test/java/com/github/dockerjava/core/command/SaveImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/SaveImageCmdImplTest.java new file mode 100644 index 00000000..859a76cc --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/SaveImageCmdImplTest.java @@ -0,0 +1,62 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.InputStream; +import java.lang.reflect.Method; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +@Test(groups = "integration") +public class SaveImageCmdImplTest extends AbstractDockerClientTest { + public static final Logger LOG = LoggerFactory + .getLogger(SaveImageCmdImplTest.class); + + String username; + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void saveImage() throws Exception { + + InputStream image = IOUtils.toBufferedInputStream(dockerClient + .saveImageCmd("busybox").exec()); + assertThat(image.available(), greaterThan(0)); + + } + +} diff --git a/src/test/java/com/github/dockerjava/core/command/SearchImagesCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/SearchImagesCmdImplTest.java new file mode 100644 index 00000000..5c239353 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/SearchImagesCmdImplTest.java @@ -0,0 +1,62 @@ +package com.github.dockerjava.core.command; + +import static ch.lambdaj.Lambda.filter; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; + +import java.lang.reflect.Method; +import java.util.List; + +import org.hamcrest.Matcher; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.model.SearchItem; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class SearchImagesCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void searchImages() throws DockerException { + List dockerSearch = dockerClient.searchImagesCmd("busybox").exec(); + LOG.info("Search returned {}", dockerSearch.toString()); + + Matcher matcher = hasItem(hasField("name", equalTo("busybox"))); + assertThat(dockerSearch, matcher); + + assertThat( + filter(hasField("name", is("busybox")), dockerSearch).size(), + equalTo(1)); + } + + +} diff --git a/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java new file mode 100644 index 00000000..045c008e --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java @@ -0,0 +1,654 @@ +package com.github.dockerjava.core.command; + +import static com.github.dockerjava.api.model.AccessMode.ro; +import static com.github.dockerjava.api.model.Capability.*; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; + +import java.lang.reflect.Method; +import java.util.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.InternalServerErrorException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.StartContainerCmd; +import com.github.dockerjava.api.model.*; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class StartContainerCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void startContainerWithVolumes() throws DockerException { + + // see http://docs.docker.io/use/working_with_volumes/ + Volume volume1 = new Volume("/opt/webapp1"); + + Volume volume2 = new Volume("/opt/webapp2"); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withVolumes(volume1, volume2) + .withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getConfig().getVolumes().keySet(), + contains("/opt/webapp1", "/opt/webapp2")); + + dockerClient + .startContainerCmd(container.getId()) + .withBinds(new Bind("/src/webapp1", volume1, ro), + new Bind("/src/webapp2", volume2)).exec(); + + dockerClient.waitContainerCmd(container.getId()).exec(); + + inspectContainerResponse = dockerClient.inspectContainerCmd( + container.getId()).exec(); + + assertContainerHasVolumes(inspectContainerResponse, volume1, volume2); + + assertThat( + Arrays.asList(inspectContainerResponse.getVolumesRW()), + contains(new VolumeRW(volume1, AccessMode.ro), new VolumeRW( + volume2))); + + } + + @Test + public void startContainerWithVolumesFrom() throws DockerException { + + Volume volume1 = new Volume("/opt/webapp1"); + Volume volume2 = new Volume("/opt/webapp2"); + + String container1Name = UUID.randomUUID().toString(); + + CreateContainerResponse container1 = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999") + .withName(container1Name).exec(); + LOG.info("Created container1 {}", container1.toString()); + + dockerClient + .startContainerCmd(container1.getId()) + .withBinds(new Bind("/src/webapp1", volume1), + new Bind("/src/webapp2", volume2)).exec(); + LOG.info("Started container1 {}", container1.toString()); + + InspectContainerResponse inspectContainerResponse1 = dockerClient + .inspectContainerCmd(container1.getId()).exec(); + + assertContainerHasVolumes(inspectContainerResponse1, volume1, volume2); + + CreateContainerResponse container2 = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container2 {}", container2.toString()); + + dockerClient.startContainerCmd(container2.getId()) + .withVolumesFrom(container1Name).exec(); + LOG.info("Started container2 {}", container2.toString()); + + InspectContainerResponse inspectContainerResponse2 = dockerClient + .inspectContainerCmd(container2.getId()).exec(); + + assertContainerHasVolumes(inspectContainerResponse2, volume1, volume2); + } + + @Test + public void startContainerWithDns() throws DockerException { + + String aDnsServer = "8.8.8.8"; + String anotherDnsServer = "8.8.4.4"; + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()) + .withDns(aDnsServer, anotherDnsServer).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig() + .getDns()), contains(aDnsServer, anotherDnsServer)); + } + + @Test + public void startContainerWithDnsSearch() throws DockerException { + + String dnsSearch = "example.com"; + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + dockerClient.startContainerCmd(container.getId()) + .withDnsSearch(dnsSearch).exec(); + + inspectContainerResponse = dockerClient.inspectContainerCmd( + container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig() + .getDnsSearch()), contains(dnsSearch)); + } + + @Test + public void startContainerWithPortBindings() throws DockerException { + + ExposedPort tcp22 = ExposedPort.tcp(22); + ExposedPort tcp23 = ExposedPort.tcp(23); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("true") + .withExposedPorts(tcp22, tcp23).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + Ports portBindings = new Ports(); + portBindings.bind(tcp22, Ports.Binding(11022)); + portBindings.bind(tcp23, Ports.Binding(11023)); + portBindings.bind(tcp23, Ports.Binding(11024)); + + dockerClient.startContainerCmd(container.getId()) + .withPortBindings(portBindings).exec(); + + inspectContainerResponse = dockerClient.inspectContainerCmd( + container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getConfig() + .getExposedPorts()), contains(tcp22, tcp23)); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings() + .getBindings().get(tcp22)[0], is(equalTo(Ports.Binding(11022)))); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings() + .getBindings().get(tcp23)[0], is(equalTo(Ports.Binding(11023)))); + + assertThat(inspectContainerResponse.getHostConfig().getPortBindings() + .getBindings().get(tcp23)[1], is(equalTo(Ports.Binding(11024)))); + + } + + @Test + public void startContainerWithRandomPortBindings() throws DockerException { + + ExposedPort tcp22 = ExposedPort.tcp(22); + ExposedPort tcp23 = ExposedPort.tcp(23); + + Ports portBindings = new Ports(); + portBindings.bind(tcp22, Ports.Binding(null)); + portBindings.bind(tcp23, Ports.Binding(null)); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999") + .withExposedPorts(tcp22, tcp23).withPortBindings(portBindings) + .withPublishAllPorts(true).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse.getConfig() + .getExposedPorts()), contains(tcp22, tcp23)); + + assertThat(inspectContainerResponse.getNetworkSettings().getPorts() + .getBindings().get(tcp22)[0].getHostPort(), is(notNullValue())); + + assertThat(inspectContainerResponse.getNetworkSettings().getPorts() + .getBindings().get(tcp23)[0], is(notNullValue())); + + } + + @Test + public void startContainerWithConflictingPortBindings() + throws DockerException { + + ExposedPort tcp22 = ExposedPort.tcp(22); + ExposedPort tcp23 = ExposedPort.tcp(23); + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("true") + .withExposedPorts(tcp22, tcp23).exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + Ports portBindings = new Ports(); + portBindings.bind(tcp22, Ports.Binding(11022)); + portBindings.bind(tcp23, Ports.Binding(11022)); + + try { + dockerClient.startContainerCmd(container.getId()) + .withPortBindings(portBindings).exec(); + fail("expected InternalServerErrorException"); + } catch (InternalServerErrorException e) { + + } + + } + + @Test + public void startContainerWithLinkingDeprecated() throws DockerException { + + CreateContainerResponse container1 = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container1").exec(); + + LOG.info("Created container1 {}", container1.toString()); + assertThat(container1.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + InspectContainerResponse inspectContainerResponse1 = dockerClient + .inspectContainerCmd(container1.getId()).exec(); + LOG.info("Container1 Inspect: {}", inspectContainerResponse1.toString()); + + assertThat(inspectContainerResponse1.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse1.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getId(), + startsWith(container1.getId())); + assertThat(inspectContainerResponse1.getName(), equalTo("/container1")); + assertThat(inspectContainerResponse1.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getState(), is(notNullValue())); + assertThat(inspectContainerResponse1.getState().isRunning(), is(true)); + + if (!inspectContainerResponse1.getState().isRunning()) { + assertThat(inspectContainerResponse1.getState().getExitCode(), + is(equalTo(0))); + } + + CreateContainerResponse container2 = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container2").exec(); + + LOG.info("Created container2 {}", container2.toString()); + assertThat(container2.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container2.getId()) + .withLinks(new Link("container1", "container1Link")).exec(); + + InspectContainerResponse inspectContainerResponse2 = dockerClient + .inspectContainerCmd(container2.getId()).exec(); + LOG.info("Container2 Inspect: {}", inspectContainerResponse2.toString()); + + assertThat(inspectContainerResponse2.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse2.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse2.getHostConfig(), + is(notNullValue())); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), + is(notNullValue())); + assertThat( + inspectContainerResponse2.getHostConfig().getLinks(), + equalTo(new Link[] { new Link("container1", "container1Link") })); + assertThat(inspectContainerResponse2.getId(), + startsWith(container2.getId())); + assertThat(inspectContainerResponse2.getName(), equalTo("/container2")); + assertThat(inspectContainerResponse2.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse2.getState(), is(notNullValue())); + assertThat(inspectContainerResponse2.getState().isRunning(), is(true)); + + } + + @Test + public void startContainerWithLinking() throws DockerException { + + CreateContainerResponse container1 = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container1").exec(); + + LOG.info("Created container1 {}", container1.toString()); + assertThat(container1.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container1.getId()).exec(); + + InspectContainerResponse inspectContainerResponse1 = dockerClient + .inspectContainerCmd(container1.getId()).exec(); + LOG.info("Container1 Inspect: {}", inspectContainerResponse1.toString()); + + assertThat(inspectContainerResponse1.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse1.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getId(), + startsWith(container1.getId())); + assertThat(inspectContainerResponse1.getName(), equalTo("/container1")); + assertThat(inspectContainerResponse1.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse1.getState(), is(notNullValue())); + assertThat(inspectContainerResponse1.getState().isRunning(), is(true)); + + if (!inspectContainerResponse1.getState().isRunning()) { + assertThat(inspectContainerResponse1.getState().getExitCode(), + is(equalTo(0))); + } + + CreateContainerResponse container2 = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999") + .withName("container2") + .withLinks(new Link("container1", "container1Link")).exec(); + + LOG.info("Created container2 {}", container2.toString()); + assertThat(container2.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container2.getId()).exec(); + + InspectContainerResponse inspectContainerResponse2 = dockerClient + .inspectContainerCmd(container2.getId()).exec(); + LOG.info("Container2 Inspect: {}", inspectContainerResponse2.toString()); + + assertThat(inspectContainerResponse2.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse2.getId(), not(isEmptyString())); + assertThat(inspectContainerResponse2.getHostConfig(), + is(notNullValue())); + assertThat(inspectContainerResponse2.getHostConfig().getLinks(), + is(notNullValue())); + assertThat( + inspectContainerResponse2.getHostConfig().getLinks(), + equalTo(new Link[] { new Link("container1", "container1Link") })); + assertThat(inspectContainerResponse2.getId(), + startsWith(container2.getId())); + assertThat(inspectContainerResponse2.getName(), equalTo("/container2")); + assertThat(inspectContainerResponse2.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse2.getState(), is(notNullValue())); + assertThat(inspectContainerResponse2.getState().isRunning(), is(true)); + + } + + @Test + public void startContainer() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd(new String[] { "top" }) + .exec(); + + LOG.info("Created container {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + assertThat(inspectContainerResponse.getConfig(), is(notNullValue())); + assertThat(inspectContainerResponse.getId(), not(isEmptyString())); + + assertThat(inspectContainerResponse.getId(), + startsWith(container.getId())); + + assertThat(inspectContainerResponse.getImageId(), not(isEmptyString())); + assertThat(inspectContainerResponse.getState(), is(notNullValue())); + + assertThat(inspectContainerResponse.getState().isRunning(), is(true)); + + if (!inspectContainerResponse.getState().isRunning()) { + assertThat(inspectContainerResponse.getState().getExitCode(), + is(equalTo(0))); + } + } + + @Test + public void testStartNonExistingContainer() throws DockerException { + try { + dockerClient.startContainerCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + + /** + * This tests support for --net option for the docker run command: + * --net="bridge" Set the Network mode for the container 'bridge': creates a + * new network stack for the container on the docker bridge 'none': no + * networking for this container 'container:': reuses another container + * network stack 'host': use the host network stack inside the container. + * Note: the host mode gives the container full access to local system + * services such as D-bus and is therefore considered insecure. + */ + @Test + public void startContainerWithNetworkMode() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + dockerClient.startContainerCmd(container.getId()) + .withNetworkMode("host").exec(); + + inspectContainerResponse = dockerClient.inspectContainerCmd( + container.getId()).exec(); + + assertThat(inspectContainerResponse.getHostConfig().getNetworkMode(), + is(equalTo("host"))); + } + + @Test + public void startContainerWithCapAddAndCapDrop() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).withCapAdd(NET_ADMIN) + .withCapDrop(MKNOD).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getState().isRunning(), is(true)); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig() + .getCapAdd()), contains(NET_ADMIN)); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig() + .getCapDrop()), contains(MKNOD)); + } + + @Test + public void startContainerWithDevices() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()) + .withDevices(new Device("rwm", "/dev/nulo", "/dev/zero")) + .exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getState().isRunning(), is(true)); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig() + .getDevices()), contains(new Device("rwm", "/dev/nulo", + "/dev/zero"))); + } + + @Test + public void startContainerWithExtraHosts() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()) + .withExtraHosts("dockerhost:127.0.0.1").exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getState().isRunning(), is(true)); + + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig() + .getExtraHosts()), contains("dockerhost:127.0.0.1")); + } + + @Test + public void startContainerWithRestartPolicy() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + RestartPolicy restartPolicy = RestartPolicy.onFailureRestart(5); + + dockerClient.startContainerCmd(container.getId()) + .withRestartPolicy(restartPolicy).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + assertThat(inspectContainerResponse.getState().isRunning(), is(true)); + + assertThat(inspectContainerResponse.getHostConfig().getRestartPolicy(), + is(equalTo(restartPolicy))); + } + + @Test + public void existingHostConfigIsPreservedByBlankStartCmd() + throws DockerException { + + String dnsServer = "8.8.8.8"; + + // prepare a container with custom DNS + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withDns(dnsServer) + .withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + // start container _without_any_customization_ (important!) + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + // The DNS setting survived. + assertThat(inspectContainerResponse.getHostConfig().getDns(), + is(notNullValue())); + assertThat(Arrays.asList(inspectContainerResponse.getHostConfig() + .getDns()), contains(dnsServer)); + } + + @Test + public void existingHostConfigIsResetByConfiguredStartCmd() + throws DockerException { + // As of version 1.3.2, Docker assumes that you either configure a + // container + // when creating it or when starting it, but not mixing both. + // See https://github.com/docker-java/docker-java/pull/111 + // If this test starts to fail, this behavior changed and a review of + // implementation + // and documentation might be needed. + + String dnsServer = "8.8.8.8"; + + // prepare a container with custom DNS + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withDns(dnsServer) + .withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + // modify another setting in start command. Leave DNS unchanged. + dockerClient.startContainerCmd(container.getId()) + .withPublishAllPorts(true).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + + // although start did not modify DNS Settings, they were reset to their + // default. + assertThat(inspectContainerResponse.getHostConfig().getDns(), + is(nullValue(String[].class))); + } + + @Test + public void anUnconfiguredCommandSerializesToEmptyJson() throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + StartContainerCmd command = dockerClient.startContainerCmd(""); + assertThat(objectMapper.writeValueAsString(command), is("{}")); + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/StopContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/StopContainerCmdImplTest.java new file mode 100644 index 00000000..3cf643c4 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/StopContainerCmdImplTest.java @@ -0,0 +1,82 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class StopContainerCmdImplTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory + .getLogger(StopContainerCmdImplTest.class); + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test(groups = "ignoreInCircleCi") + public void testStopContainer() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + dockerClient.startContainerCmd(container.getId()).exec(); + + LOG.info("Stopping container: {}", container.getId()); + dockerClient.stopContainerCmd(container.getId()).withTimeout(2).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + assertThat(inspectContainerResponse.getState().isRunning(), is(equalTo(false))); + assertThat(inspectContainerResponse.getState().getExitCode(), not(equalTo(0))); + } + + @Test + public void testStopNonExistingContainer() throws DockerException { + try { + dockerClient.stopContainerCmd("non-existing").withTimeout(2).exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + + } + } + +} diff --git a/src/test/java/com/github/dockerjava/core/command/TagImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/TagImageCmdImplTest.java new file mode 100644 index 00000000..97f422f2 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/TagImageCmdImplTest.java @@ -0,0 +1,65 @@ +package com.github.dockerjava.core.command; + +import java.lang.reflect.Method; + +import org.apache.commons.lang.math.RandomUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class TagImageCmdImplTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory + .getLogger(TagImageCmdImplTest.class); + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void tagImage() throws Exception { + String tag = "" + RandomUtils.nextInt(Integer.MAX_VALUE); + + dockerClient.tagImageCmd("busybox:latest", "docker-java/busybox", tag).exec(); + + dockerClient.removeImageCmd("docker-java/busybox:" + tag).exec(); + } + + @Test + public void tagNonExistingImage() throws Exception { + String tag = "" + RandomUtils.nextInt(Integer.MAX_VALUE); + + try { + dockerClient.tagImageCmd("non-existing", "docker-java/busybox", tag).exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } + +} + diff --git a/src/test/java/com/github/dockerjava/core/command/VersionCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/VersionCmdImplTest.java new file mode 100644 index 00000000..8c906434 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/VersionCmdImplTest.java @@ -0,0 +1,53 @@ +package com.github.dockerjava.core.command; + +import java.lang.reflect.Method; + +import org.apache.commons.lang.StringUtils; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.model.Version; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class VersionCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void version() throws DockerException { + Version version = dockerClient.versionCmd().exec(); + LOG.info(version.toString()); + + assertTrue(version.getGoVersion().length() > 0); + assertTrue(version.getVersion().length() > 0); + + assertEquals(StringUtils.split(version.getVersion(), ".").length, 3); + + } + + +} diff --git a/src/test/java/com/github/dockerjava/core/command/WaitContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/WaitContainerCmdImplTest.java new file mode 100644 index 00000000..19c7057d --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/WaitContainerCmdImplTest.java @@ -0,0 +1,79 @@ +package com.github.dockerjava.core.command; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +import java.lang.reflect.Method; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.DockerException; +import com.github.dockerjava.api.NotFoundException; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.client.AbstractDockerClientTest; + +@Test(groups = "integration") +public class WaitContainerCmdImplTest extends AbstractDockerClientTest { + + @BeforeTest + public void beforeTest() throws DockerException { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + } + + @AfterMethod + public void afterMethod(ITestResult result) { + super.afterMethod(result); + } + + @Test + public void testWaitContainer() throws DockerException { + + CreateContainerResponse container = dockerClient + .createContainerCmd("busybox").withCmd("true").exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()).exec(); + + int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(); + LOG.info("Container exit code: {}", exitCode); + + assertThat(exitCode, equalTo(0)); + + InspectContainerResponse inspectContainerResponse = dockerClient + .inspectContainerCmd(container.getId()).exec(); + LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); + + assertThat(inspectContainerResponse.getState().isRunning(), is(equalTo(false))); + assertThat(inspectContainerResponse.getState().getExitCode(), is(equalTo(exitCode))); + } + + @Test + public void testWaitNonExistingContainer() throws DockerException { + try { + dockerClient.waitContainerCmd("non-existing").exec(); + fail("expected NotFoundException"); + } catch (NotFoundException e) { + } + } +} diff --git a/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileTest.java b/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileTest.java new file mode 100644 index 00000000..428511c2 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileTest.java @@ -0,0 +1,50 @@ +package com.github.dockerjava.core.dockerfile; + +import junit.framework.TestCase; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class DockerfileTest extends TestCase { + + private static final Logger log = LoggerFactory.getLogger(DockerfileTest.class); + + @Test + public void testAllItems() throws IOException { + File baseDir = new File(Thread.currentThread().getContextClassLoader() + .getResource("netcat").getFile()); + + File root = baseDir.getParentFile(); + + Map dockerfiles = new HashMap(); + Map results = new HashMap(); + + for (File child : root.listFiles()) { + if (new File(child, "Dockerfile").exists()) { + Dockerfile dockerfile = new Dockerfile(new File(child, "Dockerfile")); + dockerfiles.put(child.getName(), dockerfile); + } + } + + for (String name : dockerfiles.keySet()) { + log.info("Scanning {}", name); + try { + results.put(name, dockerfiles.get(name).parse()); + } catch (Exception ex) { + log.error("Error in {}", name, ex); + } + + } + + for (String name : results.keySet()) { + log.info("Name: {} = {}", name, results.get(name)); + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/github/dockerjava/test/serdes/AbstractJSONResourceRef.java b/src/test/java/com/github/dockerjava/test/serdes/AbstractJSONResourceRef.java new file mode 100644 index 00000000..882b3b01 --- /dev/null +++ b/src/test/java/com/github/dockerjava/test/serdes/AbstractJSONResourceRef.java @@ -0,0 +1,31 @@ +/* + * Copyright 2015 CloudBees Inc., Oleg Nenashev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.dockerjava.test.serdes; + +/** + * Default implementation of the Resource reference. + * @author Oleg Nenashev + */ +public abstract class AbstractJSONResourceRef implements JSONResourceRef { + /** + * Gets a class which stores resources. + * @return Reference class by default. + */ + @Override + public Class getResourceClass() { + return this.getClass(); + } +} diff --git a/src/test/java/com/github/dockerjava/test/serdes/JSONResourceRef.java b/src/test/java/com/github/dockerjava/test/serdes/JSONResourceRef.java new file mode 100644 index 00000000..96e5df72 --- /dev/null +++ b/src/test/java/com/github/dockerjava/test/serdes/JSONResourceRef.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Oleg Nenashev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.dockerjava.test.serdes; + +import java.io.IOException; + +/** + * References JSON resources, which + * @author Oleg Nenashev + */ +public interface JSONResourceRef { + + /** + * Gets the resource file name under the class. + * @return File name, which is stored under the resource class + */ + String getFileName(); + + /** + * Gets a class which stores resources. + * @return Class to be used as a resource source + */ + Class getResourceClass(); +} diff --git a/src/test/java/com/github/dockerjava/test/serdes/JSONTestHelper.java b/src/test/java/com/github/dockerjava/test/serdes/JSONTestHelper.java new file mode 100644 index 00000000..b6a33b72 --- /dev/null +++ b/src/test/java/com/github/dockerjava/test/serdes/JSONTestHelper.java @@ -0,0 +1,118 @@ +/* + * Copyright 2015 Oleg Nenashev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.dockerjava.test.serdes; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.command.CommandJSONSamples; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + * Provides helper methods for serialization-deserialization tests + * @author Oleg Nenashev + * @since TODO + */ +public class JSONTestHelper { + + /** + * Reads JSON String from the specified resource + * @param resource JSON File + * @return JSON String + * @throws IOException JSON Conversion error + */ + public static String readString(JSONResourceRef resource) throws IOException { + InputStream istream = CommandJSONSamples.class.getResourceAsStream(resource.getFileName()); + if (istream == null) { + throw new IOException("Cannot retrieve resource " + resource.getFileName()); + } + return IOUtils.toString(istream, "UTF-8"); + } + + /** + * Reads item from the resource. + * @param Data class to be read + * @param resource Resource reference + * @param tclass Class entry + * @return Item + * @throws IOException JSON conversion error + */ + public static TClass readObject(JSONResourceRef resource, Class tclass) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + String str = readString(resource); + return mapper.readValue(str, tclass); + } + + /** + * Basic serialization-deserialization consistency test for the resource. + * @param Data class + * @param resource Resource reference + * @param tclass Class entry + * @throws IOException JSON conversion error + * @throws AssertionError Validation error + * @return Deserialized object after the roundtrip + */ + public static TClass testRoundTrip(JSONResourceRef resource, Class tclass) + throws IOException, AssertionError { + TClass item = readObject(resource, tclass); + assertNotNull(item); + return testRoundTrip(item, tclass); + } + + /** + * Performs roundtrip test for the specified class. + * @param Item class + * @param item Item to be checked + * @return Deserialized object after the roundtrip + * @throws IOException JSON Conversion error + * @throws AssertionError Validation error + */ + @SuppressWarnings("unchecked") + public static TClass testRoundTrip(TClass item) + throws IOException, AssertionError { + return testRoundTrip(item, (Class)item.getClass()); + } + + /** + * Performs roundtrip test for the specified class. + * @param Item class + * @param item Item to be checked + * @param asclass Class to be used during conversions + * @return Deserialized object after the roundtrip + * @throws IOException JSON Conversion error + * @throws AssertionError Validation error + */ + public static TClass testRoundTrip(TClass item, Class asclass) + throws IOException, AssertionError { + ObjectMapper mapper = new ObjectMapper(); + + String serialized1 = mapper.writeValueAsString(item); + JsonNode json1 = mapper.readTree(serialized1); + TClass deserialized1 = mapper.readValue(serialized1, asclass); + String serialized2 = mapper.writeValueAsString(deserialized1); + JsonNode json2 = mapper.readTree(serialized2); + TClass deserialized2 = mapper.readValue(serialized2, asclass); + + assertEquals(json2, json1, "JSONs must be equal after the second roundtrip"); + return deserialized2; + } +} diff --git a/src/test/java/com/kpelykh/docker/client/test/AbstractDockerClientTest.java b/src/test/java/com/kpelykh/docker/client/test/AbstractDockerClientTest.java deleted file mode 100644 index d8313ca4..00000000 --- a/src/test/java/com/kpelykh/docker/client/test/AbstractDockerClientTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.kpelykh.docker.client.test; - -import com.kpelykh.docker.client.DockerClient; -import com.kpelykh.docker.client.DockerException; -import com.sun.jersey.api.client.ClientResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.ITestResult; - -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -public abstract class AbstractDockerClientTest extends Assert { - - public static final Logger LOG = LoggerFactory - .getLogger(AbstractDockerClientTest.class); - - protected DockerClient dockerClient; - - protected List tmpImgs; - protected List tmpContainers; - - - public void beforeTest() throws DockerException { - LOG.info("======================= BEFORETEST ======================="); - String url = System.getProperty("docker.url", "http://localhost:4243"); - LOG.info("Connecting to Docker server at " + url); - dockerClient = new DockerClient(url); - - LOG.info("Pulling image 'busybox'"); - // need to block until image is pulled completely - logResponseStream(dockerClient.pull("busybox")); - - - - assertNotNull(dockerClient); - LOG.info("======================= END OF BEFORETEST =======================\n\n"); - } - - public void afterTest() { - LOG.info("======================= END OF AFTERTEST ======================="); - } - - - public void beforeMethod(Method method) { - tmpContainers = new ArrayList(); - tmpImgs = new ArrayList(); - LOG.info(String - .format("################################## STARTING %s ##################################", - method.getName())); - } - - public void afterMethod(ITestResult result) { - - for (String container : tmpContainers) { - LOG.info("Cleaning up temporary container {}", container); - try { - dockerClient.stopContainer(container); - dockerClient.kill(container); - dockerClient.removeContainer(container); - } catch (DockerException ignore) { - } - } - - for (String image : tmpImgs) { - LOG.info("Cleaning up temporary image {}", image); - try { - dockerClient.removeImage(image); - } catch (DockerException ignore) { - } - } - - LOG.info( - "################################## END OF {} ##################################\n", - result.getName()); - } - - protected String logResponseStream(ClientResponse response) { - String responseString; - try { - responseString = DockerClient.asString(response); - } catch (IOException e) { - throw new RuntimeException(e); - } - LOG.info("Container log: {}", responseString); - return responseString; - } - -} diff --git a/src/test/java/com/kpelykh/docker/client/test/DockerClientAuthTest.java b/src/test/java/com/kpelykh/docker/client/test/DockerClientAuthTest.java deleted file mode 100644 index 8c7fc4fa..00000000 --- a/src/test/java/com/kpelykh/docker/client/test/DockerClientAuthTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.kpelykh.docker.client.test; - -import com.kpelykh.docker.client.DockerException; -import com.sun.jersey.api.client.UniformInterfaceException; - -import org.hamcrest.Matchers; -import org.testng.ITestResult; -import org.testng.annotations.*; - -import java.lang.reflect.Method; - -import static org.hamcrest.MatcherAssert.assertThat; - -public class DockerClientAuthTest extends AbstractDockerClientTest { - - @BeforeTest - public void beforeTest() throws DockerException { - super.beforeTest(); - } - @AfterTest - public void afterTest() { - super.afterTest(); - } - - @BeforeMethod - public void beforeMethod(Method method) { - super.beforeMethod(method); - } - - @AfterMethod - public void afterMethod(ITestResult result) { - super.afterMethod(result); - } - - @Test - public void testAuth() throws Exception { - dockerClient.auth(); - } - - @Test - public void testAuthInvalid() throws Exception { - System.setProperty("docker.io.password", "garbage"); - try { - dockerClient.auth(); - fail(); - } catch (DockerException e) { - assertThat(e.getCause(), Matchers.instanceOf(UniformInterfaceException.class)); - assertEquals(((UniformInterfaceException) e.getCause()).getResponse().getStatus(), 401); - } - } -} diff --git a/src/test/java/com/kpelykh/docker/client/test/DockerClientTest.java b/src/test/java/com/kpelykh/docker/client/test/DockerClientTest.java deleted file mode 100644 index f6af3823..00000000 --- a/src/test/java/com/kpelykh/docker/client/test/DockerClientTest.java +++ /dev/null @@ -1,824 +0,0 @@ -package com.kpelykh.docker.client.test; - -import static ch.lambdaj.Lambda.filter; -import static ch.lambdaj.Lambda.selectUnique; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.isEmptyString; -import static org.hamcrest.Matchers.lessThan; -import static org.hamcrest.Matchers.lessThanOrEqualTo; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.Matchers.startsWith; -import static org.testinfected.hamcrest.jpa.HasFieldWithValue.hasField; - -import java.io.File; -import java.io.IOException; -import java.io.StringWriter; -import java.lang.reflect.Method; -import java.net.DatagramSocket; -import java.net.ServerSocket; -import java.util.List; -import java.util.Random; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.LineIterator; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.math.RandomUtils; -import org.hamcrest.Matcher; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.ITestResult; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; - -import com.kpelykh.docker.client.DockerException; -import com.kpelykh.docker.client.model.ChangeLog; -import com.kpelykh.docker.client.model.CommitConfig; -import com.kpelykh.docker.client.model.Container; -import com.kpelykh.docker.client.model.ContainerConfig; -import com.kpelykh.docker.client.model.ContainerCreateResponse; -import com.kpelykh.docker.client.model.ContainerInspectResponse; -import com.kpelykh.docker.client.model.Image; -import com.kpelykh.docker.client.model.ImageInspectResponse; -import com.kpelykh.docker.client.model.Info; -import com.kpelykh.docker.client.model.Ports; -import com.kpelykh.docker.client.model.SearchItem; -import com.kpelykh.docker.client.model.Version; -import com.sun.jersey.api.client.ClientResponse; - -/** - * Unit test for DockerClient. - * - * @author Konstantin Pelykh (kpelykh@gmail.com) - */ -public class DockerClientTest extends AbstractDockerClientTest { - public static final Logger LOG = LoggerFactory - .getLogger(DockerClientTest.class); - - @BeforeTest - public void beforeTest() throws DockerException { - super.beforeTest(); - } - @AfterTest - public void afterTest() { - super.afterTest(); - } - - @BeforeMethod - public void beforeMethod(Method method) { - super.beforeMethod(method); - } - - @AfterMethod - public void afterMethod(ITestResult result) { - super.afterMethod(result); - } - - /* - * ######################### ## INFORMATION TESTS ## - * ######################### - */ - - @Test - public void testDockerVersion() throws DockerException { - Version version = dockerClient.version(); - LOG.info(version.toString()); - - assertTrue(version.getGoVersion().length() > 0); - assertTrue(version.getVersion().length() > 0); - - assertEquals(StringUtils.split(version.getVersion(), ".").length, 3); - - } - - @Test - public void testDockerInfo() throws DockerException { - Info dockerInfo = dockerClient.info(); - LOG.info(dockerInfo.toString()); - - assertTrue(dockerInfo.toString().contains("containers")); - assertTrue(dockerInfo.toString().contains("images")); - assertTrue(dockerInfo.toString().contains("debug")); - - assertTrue(dockerInfo.getContainers() > 0); - assertTrue(dockerInfo.getImages() > 0); - assertTrue(dockerInfo.getNFd() > 0); - assertTrue(dockerInfo.getNGoroutines() > 0); - assertTrue(dockerInfo.isMemoryLimit()); - } - - @Test - public void testDockerSearch() throws DockerException { - List dockerSearch = dockerClient.search("busybox"); - LOG.info("Search returned {}", dockerSearch.toString()); - - Matcher matcher = hasItem(hasField("name", equalTo("busybox"))); - assertThat(dockerSearch, matcher); - - assertThat( - filter(hasField("name", is("busybox")), dockerSearch).size(), - equalTo(1)); - } - - /* - * ################### ## LISTING TESTS ## ################### - */ - - @Test - public void testImages() throws DockerException { - List images = dockerClient.getImages(true); - assertThat(images, notNullValue()); - LOG.info("Images List: {}", images); - Info info = dockerClient.info(); - - assertThat(images.size(), equalTo(info.getImages())); - - Image img = images.get(0); - assertThat(img.getCreated(), is(greaterThan(0L))); - assertThat(img.getVirtualSize(), is(greaterThan(0L))); - assertThat(img.getId(), not(isEmptyString())); - assertThat(img.getTag(), not(isEmptyString())); - assertThat(img.getRepository(), not(isEmptyString())); - } - - @Test - public void testListContainers() throws DockerException { - - String testImage = "hackmann/empty"; - - LOG.info("Pulling image 'hackmann/empty'"); - // need to block until image is pulled completely - logResponseStream(dockerClient.pull(testImage)); - tmpImgs.add(testImage); - - List containers = dockerClient.listContainers(true); - assertThat(containers, notNullValue()); - LOG.info("Container List: {}", containers); - - int size = containers.size(); - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage(testImage); - containerConfig.setCmd(new String[] { "echo" }); - - ContainerCreateResponse container1 = dockerClient - .createContainer(containerConfig); - - assertThat(container1.getId(), not(isEmptyString())); - - ContainerInspectResponse containerInspectResponse = dockerClient.inspectContainer(container1.getId()); - - assertThat(containerInspectResponse.getConfig().getImage(), is(equalTo(testImage))); - - - dockerClient.startContainer(container1.getId()); - tmpContainers.add(container1.getId()); - - LOG.info("container id: " + container1.getId()); - - List containers2 = dockerClient.listContainers(true); - - for(Container container: containers2) { - LOG.info("listContainer: id=" + container.getId() +" image=" + container.getImage()); - } - - assertThat(size + 1, is(equalTo(containers2.size()))); - Matcher matcher = hasItem(hasField("id", startsWith(container1.getId()))); - assertThat(containers2, matcher); - - List filteredContainers = filter( - hasField("id", startsWith(container1.getId())), containers2); - assertThat(filteredContainers.size(), is(equalTo(1))); - - for(Container container: filteredContainers) { - LOG.info("filteredContainer: " + container.getImage()); - } - - Container container2 = filteredContainers.get(0); - assertThat(container2.getCommand(), not(isEmptyString())); - assertThat(container2.getImage(), equalTo(testImage + ":latest")); - } - - /* - * ##################### ## CONTAINER TESTS ## ##################### - */ - - @Test - public void testCreateContainer() throws DockerException { - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "true" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - - LOG.info("Created container {}", container.toString()); - - assertThat(container.getId(), not(isEmptyString())); - - tmpContainers.add(container.getId()); - } - - @Test - public void testStartContainer() throws DockerException { - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "true" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - LOG.info("Created container {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); - boolean add = tmpContainers.add(container.getId()); - - dockerClient.startContainer(container.getId()); - - ContainerInspectResponse containerInspectResponse = dockerClient - .inspectContainer(container.getId()); - LOG.info("Container Inspect: {}", containerInspectResponse.toString()); - - assertThat(containerInspectResponse.config, is(notNullValue())); - assertThat(containerInspectResponse.getId(), not(isEmptyString())); - - assertThat(containerInspectResponse.getId(), - startsWith(container.getId())); - - assertThat(containerInspectResponse.getImageId(), not(isEmptyString())); - assertThat(containerInspectResponse.getState(), is(notNullValue())); - - assertThat(containerInspectResponse.getState().running, is(true)); - - if (!containerInspectResponse.getState().running) { - assertThat(containerInspectResponse.getState().exitCode, - is(equalTo(0))); - } - - } - - @Test - public void testWaitContainer() throws DockerException { - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "true" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - LOG.info("Created container: {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); - tmpContainers.add(container.getId()); - - dockerClient.startContainer(container.getId()); - - int exitCode = dockerClient.waitContainer(container.getId()); - LOG.info("Container exit code: {}", exitCode); - - assertThat(exitCode, equalTo(0)); - - ContainerInspectResponse containerInspectResponse = dockerClient - .inspectContainer(container.getId()); - LOG.info("Container Inspect: {}", containerInspectResponse.toString()); - - assertThat(containerInspectResponse.getState().running, - is(equalTo(false))); - assertThat(containerInspectResponse.getState().exitCode, - is(equalTo(exitCode))); - - } - - @Test - public void testLogs() throws DockerException, IOException { - - String snippet = "hello world"; - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "/bin/echo", snippet }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - LOG.info("Created container: {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); - - dockerClient.startContainer(container.getId()); - tmpContainers.add(container.getId()); - - int exitCode = dockerClient.waitContainer(container.getId()); - - assertThat(exitCode, equalTo(0)); - - ClientResponse response = dockerClient.logContainer(container.getId()); - - assertThat(logResponseStream(response), endsWith(snippet)); - } - - @Test - public void testDiff() throws DockerException { - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "touch", "/test" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - LOG.info("Created container: {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); - dockerClient.startContainer(container.getId()); - boolean add = tmpContainers.add(container.getId()); - int exitCode = dockerClient.waitContainer(container.getId()); - assertThat(exitCode, equalTo(0)); - - List filesystemDiff = dockerClient.containerDiff(container.getId()); - LOG.info("Container DIFF: {}", filesystemDiff.toString()); - - assertThat(filesystemDiff.size(), equalTo(1)); - ChangeLog testChangeLog = selectUnique(filesystemDiff, - hasField("path", equalTo("/test"))); - - assertThat(testChangeLog, hasField("path", equalTo("/test"))); - assertThat(testChangeLog, hasField("kind", equalTo(1))); - } - - @Test - public void testStopContainer() throws DockerException { - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "sleep", "9999" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - LOG.info("Created container: {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); - dockerClient.startContainer(container.getId()); - tmpContainers.add(container.getId()); - - LOG.info("Stopping container: {}", container.getId()); - dockerClient.stopContainer(container.getId(), 2); - - ContainerInspectResponse containerInspectResponse = dockerClient - .inspectContainer(container.getId()); - LOG.info("Container Inspect: {}", containerInspectResponse.toString()); - - assertThat(containerInspectResponse.getState().running, - is(equalTo(false))); - assertThat(containerInspectResponse.getState().exitCode, - not(equalTo(0))); - } - - @Test - public void testKillContainer() throws DockerException { - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "sleep", "9999" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - LOG.info("Created container: {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); - dockerClient.startContainer(container.getId()); - tmpContainers.add(container.getId()); - - LOG.info("Killing container: {}", container.getId()); - dockerClient.kill(container.getId()); - - ContainerInspectResponse containerInspectResponse = dockerClient - .inspectContainer(container.getId()); - LOG.info("Container Inspect: {}", containerInspectResponse.toString()); - - assertThat(containerInspectResponse.getState().running, - is(equalTo(false))); - assertThat(containerInspectResponse.getState().exitCode, - not(equalTo(0))); - - } - - @Test - public void restartContainer() throws DockerException { - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "sleep", "9999" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - LOG.info("Created container: {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); - dockerClient.startContainer(container.getId()); - tmpContainers.add(container.getId()); - - ContainerInspectResponse containerInspectResponse = dockerClient - .inspectContainer(container.getId()); - LOG.info("Container Inspect: {}", containerInspectResponse.toString()); - - String startTime = containerInspectResponse.getState().startedAt; - - dockerClient.restart(container.getId(), 2); - - ContainerInspectResponse containerInspectResponse2 = dockerClient - .inspectContainer(container.getId()); - LOG.info("Container Inspect After Restart: {}", - containerInspectResponse2.toString()); - - String startTime2 = containerInspectResponse2.getState().startedAt; - - assertThat(startTime, not(equalTo(startTime2))); - - assertThat(containerInspectResponse.getState().running, - is(equalTo(true))); - - dockerClient.kill(container.getId()); - } - - @Test - public void removeContainer() throws DockerException { - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "true" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - - dockerClient.startContainer(container.getId()); - dockerClient.waitContainer(container.getId()); - tmpContainers.add(container.getId()); - - LOG.info("Removing container: {}", container.getId()); - dockerClient.removeContainer(container.getId()); - - List containers2 = dockerClient.listContainers(true); - Matcher matcher = not(hasItem(hasField("id", - startsWith(container.getId())))); - assertThat(containers2, matcher); - - } - - /* - * ################## ## IMAGES TESTS ## ################## - */ - - @Test - public void testPullImage() throws DockerException, IOException { - - // This should be an image that is not used by other repositories already - // pulled down, preferably small in size. If tag is not used pull will - // download all images in that repository but tmpImgs will only - // deleted 'latest' image but not images with other tags - String testImage = "hackmann/empty"; - - LOG.info("Removing image: {}", testImage); - dockerClient.removeImage(testImage); - - Info info = dockerClient.info(); - LOG.info("Client info: {}", info.toString()); - - int imgCount = info.getImages(); - - LOG.info("Pulling image: {}", testImage); - - tmpImgs.add(testImage); - ClientResponse response = dockerClient.pull(testImage); - - assertThat(logResponseStream(response), containsString("Download complete")); - - info = dockerClient.info(); - LOG.info("Client info after pull, {}", info.toString()); - - // TODO: imgCount should differ (maybe a docker bug?) - assertThat(imgCount, lessThanOrEqualTo(info.getImages())); - - ImageInspectResponse imageInspectResponse = dockerClient - .inspectImage(testImage); - LOG.info("Image Inspect: {}", imageInspectResponse.toString()); - assertThat(imageInspectResponse, notNullValue()); - } - - @Test - public void commitImage() throws DockerException { - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "touch", "/test" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - LOG.info("Created container: {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); - dockerClient.startContainer(container.getId()); - tmpContainers.add(container.getId()); - - LOG.info("Commiting container: {}", container.toString()); - String imageId = dockerClient - .commit(new CommitConfig(container.getId())); - tmpImgs.add(imageId); - - ImageInspectResponse imageInspectResponse = dockerClient - .inspectImage(imageId); - LOG.info("Image Inspect: {}", imageInspectResponse.toString()); - - assertThat(imageInspectResponse, - hasField("container", startsWith(container.getId()))); - assertThat(imageInspectResponse.getContainerConfig().getImage(), - equalTo("busybox")); - - ImageInspectResponse busyboxImg = dockerClient.inspectImage("busybox"); - - assertThat(imageInspectResponse.getParent(), - equalTo(busyboxImg.getId())); - } - - @Test - public void testRemoveImage() throws DockerException, InterruptedException { - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "touch", "/test" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - LOG.info("Created container: {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); - dockerClient.startContainer(container.getId()); - tmpContainers.add(container.getId()); - - LOG.info("Commiting container {}", container.toString()); - String imageId = dockerClient - .commit(new CommitConfig(container.getId())); - tmpImgs.add(imageId); - - dockerClient.stopContainer(container.getId()); - dockerClient.kill(container.getId()); - dockerClient.removeContainer(container.getId()); - - tmpContainers.remove(container.getId()); - LOG.info("Removing image: {}", imageId); - dockerClient.removeImage(imageId); - - List containers = dockerClient.listContainers(true); - Matcher matcher = not(hasItem(hasField("id", startsWith(imageId)))); - assertThat(containers, matcher); - } - - @Test - public void testTagImage() throws DockerException, InterruptedException { - String tag = String.valueOf(RandomUtils.nextInt(Integer.MAX_VALUE)); - - Integer result = dockerClient.tag("busybox:latest", "docker-java/busybox", tag, false); - assertThat(result, equalTo(Integer.valueOf(201))); - - dockerClient.removeImage("docker-java/busybox:" + tag); - } - - /* - * - * ################ ## MISC TESTS ## ################ - */ - - @Test - public void testRunShlex() throws DockerException { - - String[] commands = new String[] { - "true", - "echo \"The Young Descendant of Tepes & Septette for the Dead Princess\"", - "echo -n 'The Young Descendant of Tepes & Septette for the Dead Princess'", - "/bin/sh -c echo Hello World", "/bin/sh -c echo 'Hello World'", - "echo 'Night of Nights'", "true && echo 'Night of Nights'" }; - - for (String command : commands) { - LOG.info("Running command: [{}]", command); - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(commands); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - dockerClient.startContainer(container.getId()); - tmpContainers.add(container.getId()); - int exitcode = dockerClient.waitContainer(container.getId()); - assertThat(exitcode, equalTo(0)); - } - } - - @Test - public void testNginxDockerfileBuilder() throws DockerException, - IOException { - File baseDir = new File(Thread.currentThread().getContextClassLoader() - .getResource("nginx").getFile()); - - ClientResponse response = dockerClient.build(baseDir); - - StringWriter logwriter = new StringWriter(); - - try { - LineIterator itr = IOUtils.lineIterator( - response.getEntityInputStream(), "UTF-8"); - while (itr.hasNext()) { - String line = itr.next(); - logwriter.write(line + "\n"); - LOG.info(line); - } - } finally { - IOUtils.closeQuietly(response.getEntityInputStream()); - } - - String fullLog = logwriter.toString(); - assertThat(fullLog, containsString("Successfully built")); - - String imageId = StringUtils.substringBetween(fullLog, - "Successfully built ", "\\n\"}").trim(); - - ImageInspectResponse imageInspectResponse = dockerClient - .inspectImage(imageId); - assertThat(imageInspectResponse, not(nullValue())); - LOG.info("Image Inspect: {}", imageInspectResponse.toString()); - tmpImgs.add(imageInspectResponse.getId()); - - assertThat(imageInspectResponse.getAuthor(), - equalTo("Guillaume J. Charmes \"guillaume@dotcloud.com\"")); - } - - @Test - public void testDockerBuilderAddUrl() throws DockerException, IOException { - File baseDir = new File(Thread.currentThread().getContextClassLoader() - .getResource("testAddUrl").getFile()); - dockerfileBuild(baseDir, "docker.io"); - } - - @Test - public void testDockerBuilderAddFileInSubfolder() throws DockerException, IOException { - File baseDir = new File(Thread.currentThread().getContextClassLoader() - .getResource("testAddFileInSubfolder").getFile()); - dockerfileBuild(baseDir, "Successfully executed testrun.sh"); - } - - @Test - public void testDockerBuilderAddFolder() throws DockerException, - IOException { - File baseDir = new File(Thread.currentThread().getContextClassLoader() - .getResource("testAddFolder").getFile()); - dockerfileBuild(baseDir, "Successfully executed testAddFolder.sh"); - } - - @Test - public void testNetCatDockerfileBuilder() throws DockerException, - IOException, InterruptedException { - File baseDir = new File(Thread.currentThread().getContextClassLoader() - .getResource("netcat").getFile()); - - ClientResponse response = dockerClient.build(baseDir); - - StringWriter logwriter = new StringWriter(); - - try { - LineIterator itr = IOUtils.lineIterator( - response.getEntityInputStream(), "UTF-8"); - while (itr.hasNext()) { - String line = itr.next(); - logwriter.write(line + "\n"); - LOG.info(line); - } - } finally { - IOUtils.closeQuietly(response.getEntityInputStream()); - } - - String fullLog = logwriter.toString(); - assertThat(fullLog, containsString("Successfully built")); - - String imageId = StringUtils.substringBetween(fullLog, - "Successfully built ", "\\n\"}").trim(); - - ImageInspectResponse imageInspectResponse = dockerClient - .inspectImage(imageId); - assertThat(imageInspectResponse, not(nullValue())); - LOG.info("Image Inspect: {}", imageInspectResponse.toString()); - tmpImgs.add(imageInspectResponse.getId()); - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage(imageInspectResponse.getId()); - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - assertThat(container.getId(), not(isEmptyString())); - dockerClient.startContainer(container.getId()); - tmpContainers.add(container.getId()); - - ContainerInspectResponse containerInspectResponse = dockerClient - .inspectContainer(container.getId()); - - assertThat(containerInspectResponse.getId(), notNullValue()); - assertThat(containerInspectResponse.getNetworkSettings().ports, - notNullValue()); - - // No use as such if not running on the server - for (String portstr : containerInspectResponse.getNetworkSettings().ports - .getAllPorts().keySet()) { - - Ports.Port p = containerInspectResponse.getNetworkSettings().ports - .getAllPorts().get(portstr); - int port = Integer.valueOf(p.getHostPort()); - LOG.info("Checking port {} is open", port); - assertThat(available(port), is(false)); - } - dockerClient.stopContainer(container.getId(), 0); - - } - - // UTIL - - /** - * Checks to see if a specific port is available. - * - * @param port - * the port to check for availability - */ - public static boolean available(int port) { - if (port < 1100 || port > 60000) { - throw new IllegalArgumentException("Invalid start port: " + port); - } - - ServerSocket ss = null; - DatagramSocket ds = null; - try { - ss = new ServerSocket(port); - ss.setReuseAddress(true); - ds = new DatagramSocket(port); - ds.setReuseAddress(true); - return true; - } catch (IOException e) { - } finally { - if (ds != null) { - ds.close(); - } - - if (ss != null) { - try { - ss.close(); - } catch (IOException e) { - /* should not be thrown */ - } - } - } - - return false; - } - - private String dockerfileBuild(File baseDir, String expectedText) - throws DockerException, IOException { - - // Build image - ClientResponse response = dockerClient.build(baseDir); - - StringWriter logwriter = new StringWriter(); - - try { - LineIterator itr = IOUtils.lineIterator( - response.getEntityInputStream(), "UTF-8"); - while (itr.hasNext()) { - String line = itr.next(); - logwriter.write(line + "\n"); - LOG.info(line); - } - } finally { - IOUtils.closeQuietly(response.getEntityInputStream()); - } - - String fullLog = logwriter.toString(); - assertThat(fullLog, containsString("Successfully built")); - - String imageId = StringUtils.substringBetween(fullLog, - "Successfully built ", "\\n\"}").trim(); - - // Create container based on image - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage(imageId); - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - LOG.info("Created container: {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); - - dockerClient.startContainer(container.getId()); - dockerClient.waitContainer(container.getId()); - - tmpContainers.add(container.getId()); - - // Log container - ClientResponse logResponse = dockerClient.logContainer(container - .getId()); - - assertThat(logResponseStream(logResponse), containsString(expectedText)); - - return container.getId(); - } -} \ No newline at end of file diff --git a/src/test/java/com/kpelykh/docker/client/test/DockerPushTest.java b/src/test/java/com/kpelykh/docker/client/test/DockerPushTest.java deleted file mode 100644 index 7e6d88a9..00000000 --- a/src/test/java/com/kpelykh/docker/client/test/DockerPushTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.kpelykh.docker.client.test; - - -import com.kpelykh.docker.client.DockerException; -import com.kpelykh.docker.client.model.CommitConfig; -import com.kpelykh.docker.client.model.ContainerConfig; -import com.kpelykh.docker.client.model.ContainerCreateResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.ITestResult; -import org.testng.annotations.*; - -import java.lang.reflect.Method; - -import static com.kpelykh.docker.client.DockerClient.asString; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; - -// delete here : https://index.docker.io/u/alexec/busybox/delete/ -public class DockerPushTest extends AbstractDockerClientTest { - - public static final Logger LOG = LoggerFactory - .getLogger(DockerPushTest.class); - - String username; - - @BeforeTest - public void beforeTest() throws DockerException { - super.beforeTest(); - username = dockerClient.authConfig().getUsername(); - } - @AfterTest - public void afterTest() { - super.afterTest(); - } - - @BeforeMethod - public void beforeMethod(Method method) { - super.beforeMethod(method); - } - - @AfterMethod - public void afterMethod(ITestResult result) { - super.afterMethod(result); - } - - @Test - public void testPushLatest() throws Exception { - - - ContainerConfig containerConfig = new ContainerConfig(); - containerConfig.setImage("busybox"); - containerConfig.setCmd(new String[] { "true" }); - - ContainerCreateResponse container = dockerClient - .createContainer(containerConfig); - - LOG.info("Created container {}", container.toString()); - - assertThat(container.getId(), not(isEmptyString())); - - tmpContainers.add(container.getId()); - - LOG.info("Commiting container: {}", container.toString()); - CommitConfig commitConfig = new CommitConfig(container.getId()); - - commitConfig.setRepo(username + "/busybox"); - - String imageId = dockerClient.commit(commitConfig); - - logResponseStream(dockerClient.push(username + "/busybox")); - - dockerClient.removeImage(imageId); - - assertThat(asString(dockerClient.pull(username + "/busybox")), not(containsString("404"))); - } - - @Test - public void testNotExistentImage() throws Exception { - - assertThat(logResponseStream(dockerClient.push(username + "/xxx")), containsString("error")); - } - - -} - diff --git a/src/test/resources/busyboxDockerfile/Dockerfile b/src/test/resources/busyboxDockerfile/Dockerfile new file mode 100644 index 00000000..5377ac8a --- /dev/null +++ b/src/test/resources/busyboxDockerfile/Dockerfile @@ -0,0 +1,3 @@ +FROM busybox:latest + +CMD ["cat"] \ No newline at end of file diff --git a/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_empty.json b/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_empty.json new file mode 100644 index 00000000..ea31d6f3 --- /dev/null +++ b/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_empty.json @@ -0,0 +1,115 @@ +[{ + "AppArmorProfile": "", + "Args": [ ], + "Config": { + "AttachStderr": true, + "AttachStdin": true, + "AttachStdout": true, + "Cmd": [ ], + "CpuShares": 0, + "Cpuset": "", + "Domainname": "", + "Entrypoint": null, + "Env": [ ], + "ExposedPorts": { }, + "Hostname": "469e5edd8d5b", + "Image": "jenkinsci/workflow-demo", + "Labels": {}, + "MacAddress": "", + "Memory": 0, + "MemorySwap": 0, + "NetworkDisabled": false, + "OnBuild": null, + "OpenStdin": true, + "PortSpecs": null, + "StdinOnce": true, + "Tty": true, + "User": "", + "Volumes": null, + "WorkingDir": "/var/lib/jenkins/workflow-plugin-pipeline-demo" + }, + "Created": "2015-04-29T11:55:42.968262967Z", + "Driver": "aufs", + "ExecDriver": "native-0.2", + "ExecIDs": null, + "HostConfig": { + "Binds": null, + "CapAdd": null, + "CapDrop": null, + "CgroupParent": "", + "ContainerIDFile": "", + "CpuShares": 0, + "CpusetCpus": "", + "Devices": [], + "Dns": null, + "DnsSearch": null, + "ExtraHosts": null, + "IpcMode": "", + "Links": null, + "LogConfig": { + "Config": null, + "Type": "json-file" + }, + "LxcConf": [], + "Memory": 0, + "MemorySwap": 0, + "NetworkMode": "bridge", + "PidMode": "", + "PortBindings": { + "8080/tcp": [ ] + }, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "" + }, + "SecurityOpt": null, + "Ulimits": null, + "VolumesFrom": null + }, + "HostnamePath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/hostname", + "HostsPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/hosts", + "Id": "469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1", + "Image": "4300417211ebb75b48b06ed5640d641778f312072d24b37978682345cbb362b1", + "LogPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1-json.log", + "MountLabel": "", + "Name": "/desperate_babbage", + "NetworkSettings": { + "Bridge": "docker0", + "Gateway": "172.17.42.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "LinkLocalIPv6Address": "fe80::42:acff:fe11:2", + "LinkLocalIPv6PrefixLen": 64, + "MacAddress": "02:42:ac:11:00:02", + "PortMapping": null, + "Ports": { + "22/tcp": null, + "8080/tcp": [ ] + } + }, + "Path": "/bin/sh", + "ProcessLabel": "", + "ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 898, + "Restarting": false, + "Running": true, + "StartedAt": "2015-04-29T11:55:43.464717907Z" + }, + "Volumes": {}, + "VolumesRW": {} +} +] diff --git a/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_full.json b/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_full.json new file mode 100644 index 00000000..c4383ef1 --- /dev/null +++ b/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_full.json @@ -0,0 +1,153 @@ +[{ + "AppArmorProfile": "", + "Args": [ + "-c", + "/var/lib/jenkins/run.sh" + ], + "Config": { + "AttachStderr": true, + "AttachStdin": true, + "AttachStdout": true, + "Cmd": [ + "/bin/sh", + "-c", + "/var/lib/jenkins/run.sh" + ], + "CpuShares": 0, + "Cpuset": "", + "Domainname": "", + "Entrypoint": null, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "MAVEN_VERSION=3.3.1", + "JETTY_VERSION=9.2.9.v20150224", + "REV=107ea141f5c7581056c6eb53d2ccd222cdf0d58c" + ], + "ExposedPorts": { + "22/tcp": {}, + "8080/tcp": {}, + "8081/tcp": {} + }, + "Hostname": "469e5edd8d5b", + "Image": "jenkinsci/workflow-demo", + "Labels": {}, + "MacAddress": "", + "Memory": 0, + "MemorySwap": 0, + "NetworkDisabled": false, + "OnBuild": null, + "OpenStdin": true, + "PortSpecs": null, + "StdinOnce": true, + "Tty": true, + "User": "", + "Volumes": null, + "WorkingDir": "/var/lib/jenkins/workflow-plugin-pipeline-demo" + }, + "Created": "2015-04-29T11:55:42.968262967Z", + "Driver": "aufs", + "ExecDriver": "native-0.2", + "ExecIDs": null, + "HostConfig": { + "Binds": null, + "CapAdd": null, + "CapDrop": null, + "CgroupParent": "", + "ContainerIDFile": "", + "CpuShares": 0, + "CpusetCpus": "", + "Devices": [], + "Dns": null, + "DnsSearch": null, + "ExtraHosts": null, + "IpcMode": "", + "Links": null, + "LogConfig": { + "Config": null, + "Type": "json-file" + }, + "LxcConf": [], + "Memory": 0, + "MemorySwap": 0, + "NetworkMode": "bridge", + "PidMode": "", + "PortBindings": { + "8080/tcp": [ + { + "HostIp": "", + "HostPort": "8080" + } + ], + "8081/tcp": [ + { + "HostIp": "", + "HostPort": "8081" + } + ] + }, + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyRootfs": false, + "RestartPolicy": { + "MaximumRetryCount": 0, + "Name": "" + }, + "SecurityOpt": null, + "Ulimits": null, + "VolumesFrom": null + }, + "HostnamePath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/hostname", + "HostsPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/hosts", + "Id": "469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1", + "Image": "4300417211ebb75b48b06ed5640d641778f312072d24b37978682345cbb362b1", + "LogPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1-json.log", + "MountLabel": "", + "Name": "/desperate_babbage", + "NetworkSettings": { + "Bridge": "docker0", + "Gateway": "172.17.42.1", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "IPAddress": "172.17.0.2", + "IPPrefixLen": 16, + "IPv6Gateway": "", + "LinkLocalIPv6Address": "fe80::42:acff:fe11:2", + "LinkLocalIPv6PrefixLen": 64, + "MacAddress": "02:42:ac:11:00:02", + "PortMapping": null, + "Ports": { + "22/tcp": null, + "8080/tcp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "8080" + } + ], + "8081/tcp": [ + { + "HostIp": "0.0.0.0", + "HostPort": "8081" + } + ] + } + }, + "Path": "/bin/sh", + "ProcessLabel": "", + "ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/resolv.conf", + "RestartCount": 0, + "State": { + "Dead": false, + "Error": "", + "ExitCode": 0, + "FinishedAt": "0001-01-01T00:00:00Z", + "OOMKilled": false, + "Paused": false, + "Pid": 898, + "Restarting": false, + "Running": true, + "StartedAt": "2015-04-29T11:55:43.464717907Z" + }, + "Volumes": { "/foo/bar/myvol":"/path1", "/bar/foo/myvol2":"/path2" }, + "VolumesRW": { "/foo/bar/myvol": true, "/bar/foo/myvol2": false } +} +] diff --git a/src/test/resources/eventStreamReaderDockerfile/Dockerfile b/src/test/resources/eventStreamReaderDockerfile/Dockerfile new file mode 100644 index 00000000..cdd3bba7 --- /dev/null +++ b/src/test/resources/eventStreamReaderDockerfile/Dockerfile @@ -0,0 +1,5 @@ +FROM busybox:latest + +RUN true + +CMD ["true"] \ No newline at end of file diff --git a/src/test/resources/frameReaderDockerfile/Dockerfile b/src/test/resources/frameReaderDockerfile/Dockerfile new file mode 100644 index 00000000..9aa6b984 --- /dev/null +++ b/src/test/resources/frameReaderDockerfile/Dockerfile @@ -0,0 +1,11 @@ +FROM busybox:latest + +# log to stdout and stderr so we can make sure logging with FrameReader works + +RUN echo '#! /bin/sh' > cmd.sh +RUN echo 'echo "to stdout"' >> cmd.sh +RUN echo 'echo "to stderr" > /dev/stderr' >> cmd.sh +RUN echo 'sleep 1' >> cmd.sh +RUN chmod +x cmd.sh + +CMD ["./cmd.sh"] \ No newline at end of file diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml index 99f8b3d9..bdb0ee75 100644 --- a/src/test/resources/logback.xml +++ b/src/test/resources/logback.xml @@ -6,10 +6,10 @@ - - + + - + diff --git a/src/test/resources/netcat/Dockerfile b/src/test/resources/netcat/Dockerfile index 1ea35547..1974f106 100644 --- a/src/test/resources/netcat/Dockerfile +++ b/src/test/resources/netcat/Dockerfile @@ -2,7 +2,7 @@ # # VERSION 0.3 -FROM ubuntu +FROM ubuntu:latest #install netcat RUN apt-get install -y netcat diff --git a/src/test/resources/nginx/Dockerfile b/src/test/resources/nginx/Dockerfile index b0abcd67..2d0fa24c 100644 --- a/src/test/resources/nginx/Dockerfile +++ b/src/test/resources/nginx/Dockerfile @@ -2,7 +2,7 @@ # # VERSION 0.0.1 -FROM ubuntu +FROM ubuntu:latest MAINTAINER Guillaume J. Charmes "guillaume@dotcloud.com" # make sure the package repository is up to date diff --git a/src/test/resources/nonstandard/subdirectory/Dockerfile-nonstandard b/src/test/resources/nonstandard/subdirectory/Dockerfile-nonstandard new file mode 100644 index 00000000..2d0fa24c --- /dev/null +++ b/src/test/resources/nonstandard/subdirectory/Dockerfile-nonstandard @@ -0,0 +1,12 @@ +# Nginx +# +# VERSION 0.0.1 + +FROM ubuntu:latest +MAINTAINER Guillaume J. Charmes "guillaume@dotcloud.com" + +# make sure the package repository is up to date +RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list +RUN apt-get update + +RUN apt-get install -y inotify-tools nginx apache2 openssh-server \ No newline at end of file diff --git a/src/test/resources/testAddFile/Dockerfile b/src/test/resources/testAddFile/Dockerfile index 2900a5e7..8a4a6776 100644 --- a/src/test/resources/testAddFile/Dockerfile +++ b/src/test/resources/testAddFile/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu +FROM ubuntu:latest # Copy testrun.sh files into the container diff --git a/src/test/resources/testAddFileInSubfolder/Dockerfile b/src/test/resources/testAddFileInSubfolder/Dockerfile index 41d8f437..eac6b0e9 100644 --- a/src/test/resources/testAddFileInSubfolder/Dockerfile +++ b/src/test/resources/testAddFileInSubfolder/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu +FROM ubuntu:latest # Copy testrun.sh files into the container diff --git a/src/test/resources/testAddFilesViaWildcard/Dockerfile b/src/test/resources/testAddFilesViaWildcard/Dockerfile new file mode 100644 index 00000000..a9a9ad2b --- /dev/null +++ b/src/test/resources/testAddFilesViaWildcard/Dockerfile @@ -0,0 +1,9 @@ +FROM ubuntu:latest + +# Copy testrun.sh files into the container + +ADD ./folder*/* /tmp/ + +RUN cp /tmp/*.sh /usr/local/bin/ && chmod +x /usr/local/bin/*.sh + +CMD ["testrun.sh"] diff --git a/src/test/resources/testAddFilesViaWildcard/folder1/testrun.sh b/src/test/resources/testAddFilesViaWildcard/folder1/testrun.sh new file mode 100755 index 00000000..fcfb1ee9 --- /dev/null +++ b/src/test/resources/testAddFilesViaWildcard/folder1/testrun.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +/usr/local/bin/testinclude1.sh +/usr/local/bin/testinclude2.sh + diff --git a/src/test/resources/testAddFilesViaWildcard/folder2/testinclude1.sh b/src/test/resources/testAddFilesViaWildcard/folder2/testinclude1.sh new file mode 100755 index 00000000..7953ce67 --- /dev/null +++ b/src/test/resources/testAddFilesViaWildcard/folder2/testinclude1.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Successfully executed testinclude1.sh" diff --git a/src/test/resources/testAddFilesViaWildcard/ignore/testinclude2.sh b/src/test/resources/testAddFilesViaWildcard/ignore/testinclude2.sh new file mode 100755 index 00000000..259944b2 --- /dev/null +++ b/src/test/resources/testAddFilesViaWildcard/ignore/testinclude2.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Successfully executed testinclude2.sh" diff --git a/src/test/resources/testAddFolder/Dockerfile b/src/test/resources/testAddFolder/Dockerfile index b2a8e2a1..183e831c 100644 --- a/src/test/resources/testAddFolder/Dockerfile +++ b/src/test/resources/testAddFolder/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu +FROM ubuntu:latest # Copy testrun.sh files into the container diff --git a/src/test/resources/testAddUrl/Dockerfile b/src/test/resources/testAddUrl/Dockerfile index 1d76926c..ba0b91a0 100644 --- a/src/test/resources/testAddUrl/Dockerfile +++ b/src/test/resources/testAddUrl/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu +FROM ubuntu:latest # Copy testrun.sh files into the container diff --git a/src/test/resources/testAuthConfigFile/emptyFile b/src/test/resources/testAuthConfigFile/emptyFile new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/testAuthConfigFile/invalidJsonInvalidAuth b/src/test/resources/testAuthConfigFile/invalidJsonInvalidAuth new file mode 100644 index 00000000..d0863065 --- /dev/null +++ b/src/test/resources/testAuthConfigFile/invalidJsonInvalidAuth @@ -0,0 +1 @@ +{"quay.io" : { "auth" : "Zm9vOmJhcg==", "email" :"foo@example.com"}, "https://index.docker.io/v1/" : {"auth" : "bW9vbzEyMw==", "email" : "moo@example.com"}} \ No newline at end of file diff --git a/src/test/resources/testAuthConfigFile/invalidLegacyAuthLine b/src/test/resources/testAuthConfigFile/invalidLegacyAuthLine new file mode 100644 index 00000000..7cbca287 --- /dev/null +++ b/src/test/resources/testAuthConfigFile/invalidLegacyAuthLine @@ -0,0 +1,2 @@ +auth =Zm9vOmJhcg== +email = foo@example.com \ No newline at end of file diff --git a/src/test/resources/testAuthConfigFile/invalidLegacyEmailLine b/src/test/resources/testAuthConfigFile/invalidLegacyEmailLine new file mode 100644 index 00000000..72d157b1 --- /dev/null +++ b/src/test/resources/testAuthConfigFile/invalidLegacyEmailLine @@ -0,0 +1,2 @@ +auth = Zm9vOmJhcg== +email =foo@example.com \ No newline at end of file diff --git a/src/test/resources/testAuthConfigFile/invalidLegacyInvalidAuth b/src/test/resources/testAuthConfigFile/invalidLegacyInvalidAuth new file mode 100644 index 00000000..d0af331a --- /dev/null +++ b/src/test/resources/testAuthConfigFile/invalidLegacyInvalidAuth @@ -0,0 +1,2 @@ +auth = bW9vbzEyMw== +email = foo@example.com \ No newline at end of file diff --git a/src/test/resources/testAuthConfigFile/tooSmallFile b/src/test/resources/testAuthConfigFile/tooSmallFile new file mode 100644 index 00000000..abf1731a --- /dev/null +++ b/src/test/resources/testAuthConfigFile/tooSmallFile @@ -0,0 +1 @@ +auth = Zm9vOmJhcg== \ No newline at end of file diff --git a/src/test/resources/testAuthConfigFile/validJson b/src/test/resources/testAuthConfigFile/validJson new file mode 100644 index 00000000..2b47e382 --- /dev/null +++ b/src/test/resources/testAuthConfigFile/validJson @@ -0,0 +1 @@ +{"quay.io" : { "auth" : "Zm9vOmJhcg==", "email" :"foo@example.com"}, "https://index.docker.io/v1/" : {"auth" : "Zm9vMTpiYXIx", "email" : "moo@example.com"}} \ No newline at end of file diff --git a/src/test/resources/testAuthConfigFile/validLegacy b/src/test/resources/testAuthConfigFile/validLegacy new file mode 100644 index 00000000..9d4e740d --- /dev/null +++ b/src/test/resources/testAuthConfigFile/validLegacy @@ -0,0 +1,2 @@ +auth = Zm9vOmJhcg== +email = foo@example.com \ No newline at end of file diff --git a/src/test/resources/testDockerfileIgnored/.dockerignore b/src/test/resources/testDockerfileIgnored/.dockerignore new file mode 100644 index 00000000..9e00faa1 --- /dev/null +++ b/src/test/resources/testDockerfileIgnored/.dockerignore @@ -0,0 +1,2 @@ +Dockerfile +*~ \ No newline at end of file diff --git a/src/test/resources/testDockerfileIgnored/Dockerfile b/src/test/resources/testDockerfileIgnored/Dockerfile new file mode 100644 index 00000000..8a4a6776 --- /dev/null +++ b/src/test/resources/testDockerfileIgnored/Dockerfile @@ -0,0 +1,9 @@ +FROM ubuntu:latest + +# Copy testrun.sh files into the container + +ADD ./testrun.sh /tmp/ + +RUN cp /tmp/testrun.sh /usr/local/bin/ && chmod +x /usr/local/bin/testrun.sh + +CMD ["testrun.sh"] diff --git a/src/test/resources/testDockerfileIgnored/testrun.sh b/src/test/resources/testDockerfileIgnored/testrun.sh new file mode 100755 index 00000000..80b468e7 --- /dev/null +++ b/src/test/resources/testDockerfileIgnored/testrun.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Successfully executed testrun.sh" diff --git a/src/test/resources/testDockerignore/.dockerignore b/src/test/resources/testDockerignore/.dockerignore new file mode 100644 index 00000000..63d8dbd4 --- /dev/null +++ b/src/test/resources/testDockerignore/.dockerignore @@ -0,0 +1 @@ +b \ No newline at end of file diff --git a/src/test/resources/testDockerignore/Dockerfile b/src/test/resources/testDockerignore/Dockerfile new file mode 100644 index 00000000..97b93b89 --- /dev/null +++ b/src/test/resources/testDockerignore/Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:latest + +# Copy testrun.sh files into the container + +ADD ./testrun.sh /tmp/ +ADD ./a /tmp/a + +RUN cp /tmp/testrun.sh /usr/local/bin/ && chmod +x /usr/local/bin/testrun.sh + +CMD ["testrun.sh"] diff --git a/src/test/resources/testDockerignore/a/a b/src/test/resources/testDockerignore/a/a new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/testDockerignore/a/b b/src/test/resources/testDockerignore/a/b new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/testDockerignore/a/c b/src/test/resources/testDockerignore/a/c new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/testDockerignore/a/d b/src/test/resources/testDockerignore/a/d new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/testDockerignore/testrun.sh b/src/test/resources/testDockerignore/testrun.sh new file mode 100755 index 00000000..a6f7f3fe --- /dev/null +++ b/src/test/resources/testDockerignore/testrun.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo /tmp/a/* diff --git a/src/test/resources/testENVSubstitution/Dockerfile b/src/test/resources/testENVSubstitution/Dockerfile new file mode 100644 index 00000000..134f7a60 --- /dev/null +++ b/src/test/resources/testENVSubstitution/Dockerfile @@ -0,0 +1,11 @@ +FROM ubuntu:latest + +# Copy testrun.sh files into the container + +ENV variable abc123 +ADD ./testrun.sh /tmp/ +ADD ./subst-file-$variable.txt /tmp/ +COPY ./subst-file-2-${variable}.txt /tmp/ +RUN cp /tmp/testrun.sh /usr/local/bin/ && chmod +x /usr/local/bin/testrun.sh + +CMD ["testrun.sh"] diff --git a/src/test/resources/testENVSubstitution/subst-file-2-abc123.txt b/src/test/resources/testENVSubstitution/subst-file-2-abc123.txt new file mode 100644 index 00000000..ac1acee1 --- /dev/null +++ b/src/test/resources/testENVSubstitution/subst-file-2-abc123.txt @@ -0,0 +1 @@ +Old File \ No newline at end of file diff --git a/src/test/resources/testENVSubstitution/subst-file-abc123.txt b/src/test/resources/testENVSubstitution/subst-file-abc123.txt new file mode 100644 index 00000000..ac1acee1 --- /dev/null +++ b/src/test/resources/testENVSubstitution/subst-file-abc123.txt @@ -0,0 +1 @@ +Old File \ No newline at end of file diff --git a/src/test/resources/testENVSubstitution/testrun.sh b/src/test/resources/testENVSubstitution/testrun.sh new file mode 100644 index 00000000..e79c944f --- /dev/null +++ b/src/test/resources/testENVSubstitution/testrun.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +echo "Hello Word" > hello.txt +echo "hello.txt Created" + +TEST_FILE=/tmp/subst-file-abc123.txt +TEST_FILE_2=/tmp/subst-file-2-abc123.txt + +if test -f $TEST_FILE +then + + if test -f $TEST_FILE_2 + then + echo "testENVSubstitution successfully completed" + exit 0 + else + echo "$TEST_FILE_2 does not exist" + exit 1 + fi + +else + + echo "$TEST_FILE does not exist" + exit 1 + +fi + diff --git a/src/test/resources/testEnv/Dockerfile b/src/test/resources/testEnv/Dockerfile new file mode 100644 index 00000000..a0a08463 --- /dev/null +++ b/src/test/resources/testEnv/Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:latest + +ENV varA ./subFolder +ENV varB $varA/testrun.sh + +ADD $varB /tmp/ + +RUN cp /tmp/testrun.sh /usr/local/bin/ && chmod +x /usr/local/bin/testrun.sh + +CMD ["testrun.sh"] diff --git a/src/test/resources/testEnv/subFolder/testrun.sh b/src/test/resources/testEnv/subFolder/testrun.sh new file mode 100755 index 00000000..14259aa7 --- /dev/null +++ b/src/test/resources/testEnv/subFolder/testrun.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Successfully executed testrun.sh" \ No newline at end of file diff --git a/src/test/resources/testInvalidDockerignorePattern/.dockerignore b/src/test/resources/testInvalidDockerignorePattern/.dockerignore new file mode 100644 index 00000000..89be209e --- /dev/null +++ b/src/test/resources/testInvalidDockerignorePattern/.dockerignore @@ -0,0 +1,3 @@ +*~ +[a-b-c] + diff --git a/src/test/resources/testInvalidDockerignorePattern/Dockerfile b/src/test/resources/testInvalidDockerignorePattern/Dockerfile new file mode 100644 index 00000000..8a4a6776 --- /dev/null +++ b/src/test/resources/testInvalidDockerignorePattern/Dockerfile @@ -0,0 +1,9 @@ +FROM ubuntu:latest + +# Copy testrun.sh files into the container + +ADD ./testrun.sh /tmp/ + +RUN cp /tmp/testrun.sh /usr/local/bin/ && chmod +x /usr/local/bin/testrun.sh + +CMD ["testrun.sh"] diff --git a/src/test/resources/testInvalidDockerignorePattern/testrun.sh b/src/test/resources/testInvalidDockerignorePattern/testrun.sh new file mode 100755 index 00000000..80b468e7 --- /dev/null +++ b/src/test/resources/testInvalidDockerignorePattern/testrun.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Successfully executed testrun.sh" diff --git a/src/test/resources/testReadFile/Dockerfile b/src/test/resources/testReadFile/Dockerfile index 3dfe89f9..0f73b8ea 100644 --- a/src/test/resources/testReadFile/Dockerfile +++ b/src/test/resources/testReadFile/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu +FROM ubuntu:latest # Copy testrun.sh files into the container