diff --git a/.gitignore b/.gitignore index 8f1fdc779..a705c1129 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ target *.log #Ignore Test Output -test-output \ No newline at end of file +test-output +/.checkstyle diff --git a/.travis.yml b/.travis.yml index 1d78b3bb9..d055f76e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,52 @@ +sudo: required +dist: trusty language: java + +services: + - docker + jdk: - oraclejdk7 + +install: true + +env: + global: + - DOCKER_TLS_VERIFY="" + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "GonzmzvnXsTNQV+6sKtBSSPiwbpMZjxumNt5LFp1g77/afLxw9kl2EQOXbUe308vFOwRVqeY7drBvNJa8aJkTUClfMaGRjfZ9DUwm6doMKMUYrdEkYoQTcH7yDX5K5w9MT6m+Izj+BK2gB7nK3yFlYG6COeXCdFbQ4/cf3/xfRc=" + - COVERITY_SCAN_PROJECT_NAME="docker-java/docker-java" + - COVERITY_SCAN_BRANCH_PATTERN="master" + - COVERITY_SCAN_NOTIFICATION_EMAIL="kanstantsin.sha@gmail.com" + + matrix: + - repo="main" DOCKER_HOST="tcp://127.0.0.1:2375" DOCKER_VERSION="1.12.1-0~trusty" CODECOV=true + - repo="main" DOCKER_HOST="unix:///var/run/docker.sock" DOCKER_VERSION="1.12.1-0~trusty" CODECOV=true + - repo="main" DOCKER_HOST="tcp://127.0.0.1:2375" DOCKER_VERSION="1.11.2-0~trusty" DEPLOY=true COVERITY=true CODECOV=true + - repo="main" DOCKER_HOST="unix:///var/run/docker.sock" DOCKER_VERSION="1.11.2-0~trusty" CODECOV=true + - repo="main" DOCKER_HOST="tcp://127.0.0.1:2375" DOCKER_VERSION="1.10.3-0~trusty" CODECOV=true + - repo="main" DOCKER_HOST="unix:///var/run/docker.sock" DOCKER_VERSION="1.10.3-0~trusty" CODECOV=true +# - repo="testing" DOCKER_HOST="tcp://127.0.0.1:2375" +# - repo="testing" DOCKER_HOST="unix:///var/run/docker.sock" +# - repo="experimental" DOCKER_HOST="tcp://127.0.0.1:2375" +# - repo="experimental" DOCKER_HOST="unix:///var/run/docker.sock" + +cache: + directories: + - $HOME/.travis_cache + - /tmp/coverity-cache + - $HOME/.m2 # install will pollute it + before_install: - - pip install --user codecov + - pip install --user codecov + - ./.travis/travis-before-install.sh + +script: + - ./.travis/travis-script.sh + after_success: - - ./travis-after-success.sh + - ./.travis/travis-after-success.sh + +after_script: + - sudo cat /var/log/upstart/docker.log diff --git a/.travis/get-docker-com.sh b/.travis/get-docker-com.sh new file mode 100755 index 000000000..d9c0142a2 --- /dev/null +++ b/.travis/get-docker-com.sh @@ -0,0 +1,313 @@ +#!/bin/sh +set -e +# +# This script is meant for quick & easy install via: +# 'curl -sSL https://get.docker.com/ | sh' +# or: +# 'wget -qO- https://get.docker.com/ | sh' +# +# For test builds (ie. release candidates): +# 'curl -fsSL https://test.docker.com/ | sh' +# or: +# 'wget -qO- https://test.docker.com/ | sh' +# +# For experimental builds: +# 'curl -fsSL https://experimental.docker.com/ | sh' +# or: +# 'wget -qO- https://experimental.docker.com/ | sh' +# +# Docker Maintainers: +# To update this script on https://get.docker.com, +# use hack/release.sh during a normal release, +# or the following one-liner for script hotfixes: +# aws s3 cp --acl public-read hack/install.sh s3://get.docker.com/index +# + +url="https://get.docker.com/" +apt_url="https://apt.dockerproject.org" +yum_url="https://yum.dockerproject.org" +gpg_fingerprint="58118E89F3A912897C070ADBF76221572C52609D" + +key_servers=" +ha.pool.sks-keyservers.net +pgp.mit.edu +keyserver.ubuntu.com +" + +command_exists() { + command -v "$@" > /dev/null 2>&1 +} + +semverParse() { + major="${1%%.*}" + minor="${1#$major.}" + minor="${minor%%.*}" + patch="${1#$major.$minor.}" + patch="${patch%%[-.]*}" +} + +do_install() { + case "$(uname -m)" in + *64) + ;; + *) + cat >&2 <<-'EOF' + Error: you are not using a 64bit platform. + Docker currently only supports 64bit platforms. + EOF + exit 1 + ;; + esac + + user="$(id -un 2>/dev/null || true)" + + sh_c='sh -c' + if [ "$user" != 'root' ]; then + if command_exists sudo; then + sh_c='sudo -E sh -c' + elif command_exists su; then + sh_c='su -c' + else + cat >&2 <<-'EOF' + Error: this installer needs the ability to run commands as root. + We are unable to find either "sudo" or "su" available to make this happen. + EOF + exit 1 + fi + fi + + curl='' + if command_exists curl; then + curl='curl -sSL' + elif command_exists wget; then + curl='wget -qO-' + elif command_exists busybox && busybox --list-modules | grep -q wget; then + curl='busybox wget -qO-' + fi + + # check to see which repo they are trying to install from + if [ -z "$repo" ]; then + repo='main' + if [ "https://test.docker.com/" = "$url" ]; then + repo='testing' + elif [ "https://experimental.docker.com/" = "$url" ]; then + repo='experimental' + fi + fi + + # perform some very rudimentary platform detection + lsb_dist='' + dist_version='' + if command_exists lsb_release; then + lsb_dist="$(lsb_release -si)" + fi + if [ -z "$lsb_dist" ] && [ -r /etc/lsb-release ]; then + lsb_dist="$(. /etc/lsb-release && echo "$DISTRIB_ID")" + fi + if [ -z "$lsb_dist" ] && [ -r /etc/debian_version ]; then + lsb_dist='debian' + fi + if [ -z "$lsb_dist" ] && [ -r /etc/fedora-release ]; then + lsb_dist='fedora' + fi + if [ -z "$lsb_dist" ] && [ -r /etc/oracle-release ]; then + lsb_dist='oracleserver' + fi + if [ -z "$lsb_dist" ]; then + if [ -r /etc/centos-release ] || [ -r /etc/redhat-release ]; then + lsb_dist='centos' + fi + fi + if [ -z "$lsb_dist" ] && [ -r /etc/os-release ]; then + lsb_dist="$(. /etc/os-release && echo "$ID")" + fi + + lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')" + + case "$lsb_dist" in + + ubuntu) + if command_exists lsb_release; then + dist_version="$(lsb_release --codename | cut -f2)" + fi + if [ -z "$dist_version" ] && [ -r /etc/lsb-release ]; then + dist_version="$(. /etc/lsb-release && echo "$DISTRIB_CODENAME")" + fi + ;; + + debian) + dist_version="$(cat /etc/debian_version | sed 's/\/.*//' | sed 's/\..*//')" + case "$dist_version" in + 8) + dist_version="jessie" + ;; + 7) + dist_version="wheezy" + ;; + esac + ;; + + oracleserver) + # need to switch lsb_dist to match yum repo URL + lsb_dist="oraclelinux" + dist_version="$(rpm -q --whatprovides redhat-release --queryformat "%{VERSION}\n" | sed 's/\/.*//' | sed 's/\..*//' | sed 's/Server*//')" + ;; + + fedora|centos) + dist_version="$(rpm -q --whatprovides redhat-release --queryformat "%{VERSION}\n" | sed 's/\/.*//' | sed 's/\..*//' | sed 's/Server*//')" + ;; + + *) + if command_exists lsb_release; then + dist_version="$(lsb_release --codename | cut -f2)" + fi + if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then + dist_version="$(. /etc/os-release && echo "$VERSION_ID")" + fi + ;; + + + esac + + + # Run setup for each distro accordingly + case "$lsb_dist" in + ubuntu|debian) + export DEBIAN_FRONTEND=noninteractive + + did_apt_get_update= + apt_get_update() { + if [ -z "$did_apt_get_update" ]; then + ( set -x; $sh_c 'sleep 3; apt-get update' ) + did_apt_get_update=1 + fi + } + + # aufs is preferred over devicemapper; try to ensure the driver is available. + if ! grep -q aufs /proc/filesystems && ! $sh_c 'modprobe aufs'; then + if uname -r | grep -q -- '-generic' && dpkg -l 'linux-image-*-generic' | grep -qE '^ii|^hi' 2>/dev/null; then + kern_extras="linux-image-extra-$(uname -r) linux-image-extra-virtual" + + apt_get_update + ( set -x; $sh_c 'sleep 3; apt-get install -y -q '"$kern_extras" ) || true + + if ! grep -q aufs /proc/filesystems && ! $sh_c 'modprobe aufs'; then + echo >&2 'Warning: tried to install '"$kern_extras"' (for AUFS)' + echo >&2 ' but we still have no AUFS. Docker may not work. Proceeding anyways!' + ( set -x; sleep 10 ) + fi + else + echo >&2 'Warning: current kernel is not supported by the linux-image-extra-virtual' + echo >&2 ' package. We have no AUFS support. Consider installing the packages' + echo >&2 ' linux-image-virtual kernel and linux-image-extra-virtual for AUFS support.' + ( set -x; sleep 10 ) + fi + fi + + # install apparmor utils if they're missing and apparmor is enabled in the kernel + # otherwise Docker will fail to start + if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = 'Y' ]; then + if command -v apparmor_parser >/dev/null 2>&1; then + echo 'apparmor is enabled in the kernel and apparmor utils were already installed' + else + echo 'apparmor is enabled in the kernel, but apparmor_parser missing' + apt_get_update + ( set -x; $sh_c 'sleep 3; apt-get install -y -q apparmor' ) + fi + fi + + if [ ! -e /usr/lib/apt/methods/https ]; then + apt_get_update + ( set -x; $sh_c 'sleep 3; apt-get install -y -q apt-transport-https ca-certificates' ) + fi + if [ -z "$curl" ]; then + apt_get_update + ( set -x; $sh_c 'sleep 3; apt-get install -y -q curl ca-certificates' ) + curl='curl -sSL' + fi + ( + set -x + for key_server in $key_servers ; do + $sh_c "apt-key adv --keyserver hkp://${key_server}:80 --recv-keys ${gpg_fingerprint}" && break + done + $sh_c "apt-key adv -k ${gpg_fingerprint} >/dev/null" + $sh_c "mkdir -p /etc/apt/sources.list.d" + $sh_c "echo deb [arch=$(dpkg --print-architecture)] ${apt_url}/repo ${lsb_dist}-${dist_version} ${repo} > /etc/apt/sources.list.d/docker.list" + $sh_c 'sleep 3; apt-get update' + if [ -z "$DOCKER_VERSION" ]; then + $sh_c 'apt-get -o Dpkg::Options::="--force-confnew" install -y -q docker-engine' + else + $sh_c "apt-get -o Dpkg::Options::=\"--force-confnew\" install -y -q docker-engine=$DOCKER_VERSION" + fi + ) + exit 0 + ;; + + fedora|centos|oraclelinux) + $sh_c "cat >/etc/yum.repos.d/docker-${repo}.repo" <<-EOF + [docker-${repo}-repo] + name=Docker ${repo} Repository + baseurl=${yum_url}/repo/${repo}/${lsb_dist}/${dist_version} + enabled=1 + gpgcheck=1 + gpgkey=${yum_url}/gpg + EOF + if [ "$lsb_dist" = "fedora" ] && [ "$dist_version" -ge "22" ]; then + ( + set -x + $sh_c 'sleep 3; dnf -y -q install docker-engine' + ) + else + ( + set -x + $sh_c 'sleep 3; yum -y -q install docker-engine' + ) + fi + exit 0 + ;; + gentoo) + if [ "$url" = "https://test.docker.com/" ]; then + # intentionally mixed spaces and tabs here -- tabs are stripped by "<<-'EOF'", spaces are kept in the output + cat >&2 <<-'EOF' + + You appear to be trying to install the latest nightly build in Gentoo.' + The portage tree should contain the latest stable release of Docker, but' + if you want something more recent, you can always use the live ebuild' + provided in the "docker" overlay available via layman. For more' + instructions, please see the following URL:' + + https://github.com/tianon/docker-overlay#using-this-overlay' + + After adding the "docker" overlay, you should be able to:' + + emerge -av =app-emulation/docker-9999' + + EOF + exit 1 + fi + + ( + set -x + $sh_c 'sleep 3; emerge app-emulation/docker' + ) + exit 0 + ;; + esac + + # intentionally mixed spaces and tabs here -- tabs are stripped by "<<-'EOF'", spaces are kept in the output + cat >&2 <<-'EOF' + + Either your platform is not easily detectable, is not supported by this + installer script (yet - PRs welcome! [hack/install.sh]), or does not yet have + a package for Docker. Please visit the following URL for more detailed + installation instructions: + + https://docs.docker.com/engine/installation/ + + EOF + exit 1 +} + +# wrapped up in a function so that we have some protection against only getting +# half the file during "curl | sh" +do_install diff --git a/travis-after-success.sh b/.travis/travis-after-success.sh similarity index 72% rename from travis-after-success.sh rename to .travis/travis-after-success.sh index aeb3c12c7..695358122 100755 --- a/travis-after-success.sh +++ b/.travis/travis-after-success.sh @@ -1,9 +1,12 @@ #!/usr/bin/env bash -codecov -if [[ $TRAVIS_BRANCH == "master" ]] && [[ $TRAVIS_PULL_REQUEST == "false" ]]; +if [[ $CODECOV == "true" ]]; then + codecov +fi + +if [[ $TRAVIS_BRANCH == "master" ]] && [[ $TRAVIS_PULL_REQUEST == "false" ]] && [[ $DEPLOY == "true" ]]; then - cat << EOF >> ~/settings.xml + cat <> ~/settings.xml diff --git a/.travis/travis-before-install.sh b/.travis/travis-before-install.sh new file mode 100755 index 000000000..6935f7a7d --- /dev/null +++ b/.travis/travis-before-install.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash + + +sudo apt-get install -y -q ca-certificates + +export HOST_PORT=2375 +echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt + + +if [ "$FAST_BUILD" == true ]; then + echo "Fast build, skipping docker installations." + exit 0 +fi + +set -exu + +docker info +docker version + +sudo -E apt-get update +sudo -E apt-get install -q -y wget +sudo -E apt-get -q -y --purge remove docker-engine +sudo -E apt-cache policy docker-engine + +./.travis/get-docker-com.sh + +sudo -E stop docker + +#mkdir "${HOME}/.cache" || : +#pushd "${HOME}/.cache" +# wget -N "https://apt.dockerproject.org/repo/pool/main/d/docker-engine/docker-engine_${DOCKER_VERSION}_amd64.deb" +# sudo apt-get -f install +# sudo dpkg -i "$(ls *${DOCKER_VERSION}*)" +#popd +#rm -f "src/test/resources/logback.xml" +mv "src/test/resources/travis-logback.xml" "src/test/resources/logback.xml" + +# https://github.com/docker/docker/issues/18113 +sudo rm /var/lib/docker/network/files/local-kv.db + +sudo cat /etc/default/docker + +cat << EOF | sudo tee /etc/default/docker +DOCKER_OPTS="\ +--dns 8.8.8.8 \ +--dns 8.8.4.4 \ +-D \ +-H=unix:///var/run/docker.sock \ +-H=tcp://0.0.0.0:${HOST_PORT} \ +" +EOF + +sudo cat /etc/default/docker +sudo bash -c ':> /var/log/upstart/docker.log' + +date +sudo -E start docker + +tries=20 +sleep=5 +for i in $(seq 1 $tries); do + if sudo grep "API listen on" /var/log/upstart/docker.log ; then + echo "Docker started. Delay $(($i * $sleep))" + break + elif [[ $i -ge $tries ]]; then + echo "Docker didn't start. Exiting!" + sudo cat /var/log/upstart/docker.log + exit 1 + else + echo "Docker didn't start, sleeping for 5 secs..." + sleep $sleep + fi +done + + +sudo ss -antpl + +curl -V + +docker version || sudo cat /var/log/upstart/docker.log +docker info + +set +u + +cat < "${HOME}/.docker-java.properties" +registry.username=${registry_username} +registry.password=${registry_password} +registry.email=${registry_email} +registry.url=https://index.docker.io/v1/ + +EOF diff --git a/.travis/travis-script.sh b/.travis/travis-script.sh new file mode 100755 index 000000000..1cdc84fa5 --- /dev/null +++ b/.travis/travis-script.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + + + +IS_COVERITY_SCAN_BRANCH=`ruby -e "puts '${TRAVIS_BRANCH}' =~ /\\A$COVERITY_SCAN_BRANCH_PATTERN\\z/ ? 1 : 0"` + +export COVERITY_ALLOWED=true +# Verify upload is permitted +AUTH_RES=`curl -s --form project="$COVERITY_SCAN_PROJECT_NAME" --form token="$COVERITY_SCAN_TOKEN" $SCAN_URL/api/upload_permitted` +if [ "$AUTH_RES" = "Access denied" ]; then + echo -e "\033[33;1mCoverity Scan API access denied. Check COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN.\033[0m" + COVERITY_ALLOWED=false +else + AUTH=`echo $AUTH_RES | ruby -e "require 'rubygems'; require 'json'; puts JSON[STDIN.read]['upload_permitted']"` + if [ "$AUTH" = "true" ]; then + echo -e "\033[33;1mCoverity Scan analysis authorized per quota.\033[0m" + else + WHEN=`echo $AUTH_RES | ruby -e "require 'rubygems'; require 'json'; puts JSON[STDIN.read]['next_upload_permitted_at']"` + echo -e "\033[33;1mCoverity Scan analysis NOT authorized until $WHEN.\033[0m" + + COVERITY_ALLOWED=false + fi +fi + +set -ex + +if [ "${FAST_BUILD}" == "true" ]; then + if [ "$TRAVIS_PULL_REQUEST" == "false" ] && + [ "$COVERITY" == "true" ] && + [ "$IS_COVERITY_SCAN_BRANCH" = "1" ] && + [ "$COVERITY_ALLOWED" == "true" ]; then + export COVERITY_SCAN_BUILD_COMMAND="mvn package" + #curl -s "https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh" | bash + ./.travis/travisci_build_coverity_scan.sh + else + mvn package + fi +else + if [ "$TRAVIS_PULL_REQUEST" == "false" ] && + [ "$COVERITY" == "true" ] && + [ "$IS_COVERITY_SCAN_BRANCH" = "1" ] && + [ "$COVERITY_ALLOWED" == "true" ]; then + export COVERITY_SCAN_BUILD_COMMAND="mvn verify" + #curl -s "https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh" | bash + ./.travis/travisci_build_coverity_scan.sh + else + mvn verify + fi +fi diff --git a/.travis/travisci_build_coverity_scan.sh b/.travis/travisci_build_coverity_scan.sh new file mode 100755 index 000000000..074d0a46f --- /dev/null +++ b/.travis/travisci_build_coverity_scan.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +set -e + +# Environment check +echo -e "\033[33;1mNote: COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN are available on Project Settings page on scan.coverity.com\033[0m" +[ -z "$COVERITY_SCAN_PROJECT_NAME" ] && echo "ERROR: COVERITY_SCAN_PROJECT_NAME must be set" && exit 1 +[ -z "$COVERITY_SCAN_NOTIFICATION_EMAIL" ] && echo "ERROR: COVERITY_SCAN_NOTIFICATION_EMAIL must be set" && exit 1 +[ -z "$COVERITY_SCAN_BRANCH_PATTERN" ] && echo "ERROR: COVERITY_SCAN_BRANCH_PATTERN must be set" && exit 1 +[ -z "$COVERITY_SCAN_BUILD_COMMAND" ] && echo "ERROR: COVERITY_SCAN_BUILD_COMMAND must be set" && exit 1 +[ -z "$COVERITY_SCAN_TOKEN" ] && echo "ERROR: COVERITY_SCAN_TOKEN must be set" && exit 1 + +PLATFORM=`uname` +TOOL_ARCHIVE=/tmp/coverity-cache/cov-analysis-${PLATFORM}.tgz +TOOL_URL=https://scan.coverity.com/download/${PLATFORM} +TOOL_BASE=/tmp/coverity-scan-analysis +UPLOAD_URL="https://scan.coverity.com/builds" +SCAN_URL="https://scan.coverity.com" + +# Do not run on pull requests +if [ "${TRAVIS_PULL_REQUEST}" = "true" ]; then + echo -e "\033[33;1mINFO: Skipping Coverity Analysis: branch is a pull request.\033[0m" + exit 0 +fi + +# Verify this branch should run +IS_COVERITY_SCAN_BRANCH=`ruby -e "puts '${TRAVIS_BRANCH}' =~ /\\A$COVERITY_SCAN_BRANCH_PATTERN\\z/ ? 1 : 0"` +if [ "$IS_COVERITY_SCAN_BRANCH" = "1" ]; then + echo -e "\033[33;1mCoverity Scan configured to run on branch ${TRAVIS_BRANCH}\033[0m" +else + echo -e "\033[33;1mCoverity Scan NOT configured to run on branch ${TRAVIS_BRANCH}\033[0m" + exit 1 +fi + +# Verify upload is permitted +AUTH_RES=`curl -s --form project="$COVERITY_SCAN_PROJECT_NAME" --form token="$COVERITY_SCAN_TOKEN" $SCAN_URL/api/upload_permitted` +if [ "$AUTH_RES" = "Access denied" ]; then + echo -e "\033[33;1mCoverity Scan API access denied. Check COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN.\033[0m" + exit 1 +else + AUTH=`echo $AUTH_RES | ruby -e "require 'rubygems'; require 'json'; puts JSON[STDIN.read]['upload_permitted']"` + if [ "$AUTH" = "true" ]; then + echo -e "\033[33;1mCoverity Scan analysis authorized per quota.\033[0m" + else + WHEN=`echo $AUTH_RES | ruby -e "require 'rubygems'; require 'json'; puts JSON[STDIN.read]['next_upload_permitted_at']"` + echo -e "\033[33;1mCoverity Scan analysis NOT authorized until $WHEN.\033[0m" + + exit 1 + fi +fi + +mkdir -p /tmp/coverity-cache || : + +if [ ! -d $TOOL_BASE ]; then + + # verify that binary is right + if file $TOOL_ARCHIVE | grep HTML ; then + echo "Removing $TOOL_ARCHIVE" + rm -f $TOOL_ARCHIVE + fi + + # Download Coverity Scan Analysis Tool + if [ ! -e $TOOL_ARCHIVE ]; then + echo -e "\033[33;1mDownloading Coverity Scan Analysis Tool...\033[0m" + wget -nv -N -O $TOOL_ARCHIVE $TOOL_URL --post-data "project=$COVERITY_SCAN_PROJECT_NAME&token=$COVERITY_SCAN_TOKEN" + fi + + # Extract Coverity Scan Analysis Tool + echo -e "\033[33;1mExtracting Coverity Scan Analysis Tool...\033[0m" + mkdir -p $TOOL_BASE + pushd $TOOL_BASE + du -sh $TOOL_ARCHIVE + file $TOOL_ARCHIVE + file $TOOL_ARCHIVE | grep HTML && cat $TOOL_ARCHIVE || : + ls -la $TOOL_ARCHIVE + tar -xf $TOOL_ARCHIVE #|& grep -v "Ignoring unknown extended header keyword" + popd +fi + +TOOL_DIR=`find $TOOL_BASE -type d -name 'cov-analysis*'` +export PATH=$TOOL_DIR/bin:$PATH + +# Build +echo -e "\033[33;1mRunning Coverity Scan Analysis Tool...\033[0m" +COV_BUILD_OPTIONS="" +#COV_BUILD_OPTIONS="--return-emit-failures 8 --parse-error-threshold 85" +RESULTS_DIR="cov-int" +eval "${COVERITY_SCAN_BUILD_COMMAND_PREPEND}" +COVERITY_UNSUPPORTED=1 cov-build --dir $RESULTS_DIR $COV_BUILD_OPTIONS $COVERITY_SCAN_BUILD_COMMAND +cov-import-scm --dir $RESULTS_DIR --scm git --log $RESULTS_DIR/scm_log.txt 2>&1 + +# Upload results +echo -e "\033[33;1mTarring Coverity Scan Analysis results...\033[0m" +RESULTS_ARCHIVE=analysis-results.tgz +tar czf $RESULTS_ARCHIVE $RESULTS_DIR +SHA=`git rev-parse --short HEAD` + +echo -e "\033[33;1mUploading Coverity Scan Analysis results...\033[0m" +response=$(curl \ + --silent --write-out "\n%{http_code}\n" \ + --form project=$COVERITY_SCAN_PROJECT_NAME \ + --form token=$COVERITY_SCAN_TOKEN \ + --form email=$COVERITY_SCAN_NOTIFICATION_EMAIL \ + --form file=@$RESULTS_ARCHIVE \ + --form version=$SHA \ + --form description="Travis CI build" \ + $UPLOAD_URL) +status_code=$(echo "$response" | sed -n '$p') +if [ "$status_code" != "201" ]; then + TEXT=$(echo "$response" | sed '$d') + echo -e "\033[33;1mCoverity Scan upload failed: $TEXT.\033[0m" + exit 1 +fi diff --git a/CHANGELOG.md b/CHANGELOG.md index ee0dae776..8f41b2938 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,47 @@ Change Log === +## Next 3.0.7 + * Label image during build + * Expose 'User' property on ExecCreateCmd #707 #708 -3.0.0-SNAPSHOT +## 3.0.6 + * Fixed issue with jersey and unix domain sockets. + * [#703](https://github.com/docker-java/docker-java/pull/703) Allow to configure connection pool timeout. + * Make all models Serializable. + * [NETTY] Fix loadImage responce on 1.24 API. + * LogPath field for inspect container. + * [#700] (https://github.com/docker-java/docker-java/pull/700) Bugfix:donot throw RuntimeException when a error occured in awaitCompletion(long,TimeUnit) + +## 3.0.5 + * Events updated to 1.24 API model. + +## 3.0.4 + * Make cert util methods public. + +## 3.0.3 + * [JERSEY] Don't send body for start container request. + +## 3.0.2 + * Enhanced Dockerignore filtering. + * Added shmsize for hostconfig. + * Exposed HostConfig instead of spaghetty calls. + +## 3.0.1 + +All changes +* Updated all dependencies +* [#643] (https://github.com/docker-java/docker-java/pull/643) Fixes for .dockerignore filtering +* [#627] (https://github.com/docker-java/docker-java/pull/627) Implementation of POST /images/load endpoint +* [#630] (https://github.com/docker-java/docker-java/pull/630) Fix: Second execution of a docker command in Netty implementation always fails +* [#596] (https://github.com/docker-java/docker-java/pull/596) Refactor configuration of SSL to allow override with custom config +* [#529] (https://github.com/docker-java/docker-java/pull/529) Refactor CertUtils. Support ECDSA and PrivateKey +* [#593] (https://github.com/docker-java/docker-java/pull/593) Added Device.parse() method with simple verification. + +v3.0.0 --- Notes -* The upcoming release will contain multiple API breaking changes therefore the major version switch. It will support a subset of v.1.22 of the docker remote API. It also includes an experimental netty based implementation of `DockerCmdExecFactory` that probably will replace the current jersey/httpclient based one in a later release. +* The 3.0.0 release contains multiple API breaking changes compared to 2.x therefore the major version switch. It supports a subset of v.1.22 of the docker remote API. It also includes an experimental netty based implementation of `DockerCmdExecFactory` that probably will replace the current jersey/httpclient based one in a later release. Take a look at the [Wiki](https://github.com/docker-java/docker-java/wiki#intialize-docker-client-advanced) how to use it. * The configuration has been changed to better match the docker CLI configuration options. The properties file was renamed from `docker.io.properties` to `docker-java.properties`. See README.md for details. All changes diff --git a/README.md b/README.md index f2135f61b..d5052e6b9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ +[![Maven Central](https://img.shields.io/maven-central/v/com.github.docker-java/docker-java.svg)](https://mvnrepository.com/artifact/com.github.docker-java/docker-java) +[![Bintray](https://api.bintray.com/packages/kostyasha/maven/com.github.docker-java%3Adocker-java/images/download.svg)](https://bintray.com/kostyasha/maven/com.github.docker-java%3Adocker-java/_latestVersion) +[![Reference Status](https://www.versioneye.com/java/com.github.docker-java:docker-java/reference_badge.svg?style=flat)](https://www.versioneye.com/java/com.github.docker-java:docker-java/references) [![Build Status](https://travis-ci.org/docker-java/docker-java.svg?branch=master)](https://travis-ci.org/docker-java/docker-java) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/9177/badge.svg?flat=1)](https://scan.coverity.com/projects/9177) +[![codecov.io](http://codecov.io/github/docker-java/docker-java/coverage.svg?branch=master)](http://codecov.io/github/docker-java/docker-java?branch=master) +[![License](http://img.shields.io/:license-apache-blue.svg?style=flat)](https://github.com/docker-java/docker-java/blob/master/LICENSE) + # docker-java @@ -8,16 +15,15 @@ Java API client for [Docker](http://docs.docker.io/ "Docker") Developer forum for [docker-java](https://groups.google.com/forum/?#!forum/docker-java-dev "docker-java") +[Changelog](https://github.com/docker-java/docker-java/blob/master/CHANGELOG.md)
+[Wiki](https://github.com/docker-java/docker-java/wiki) + ## Build with Maven ###### Prerequisites: -* Java 1.7 -* Maven 3.0.5 - -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 +* Java min 1.7 +* Maven 3 Build and run integration tests as follows: @@ -27,63 +33,41 @@ If you do not have access to a Docker server or just want to execute the build q $ mvn clean install -DskipITs -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 +By default the docker engine is using local UNIX sockets for communication with the docker CLI so docker-java +client also uses UNIX domain sockets to connect to the docker daemon by default. To make the docker daemon listening on a TCP (http/https) port you have to configure it by setting the DOCKER_OPTS environment variable to something like the following: DOCKER_OPTS="-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock" - -However you can force docker-java to use UNIX socket communication by configure the following (see [Configuration](.#Configuration) for details): - - DOCKER_HOST=unix:///var/run/docker.sock - DOCKER_TLS_VERIFY=0 - + More details about 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: - - $ docker -H tcp://127.0.0.1:2375 version - - 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 - -Run build without integration tests: +To force docker-java to use TCP (http) configure the following (see [Configuration](https://github.com/docker-java/docker-java#configuration) for details): - $ mvn clean install -DskipITs + DOCKER_HOST=tcp://127.0.0.1:2375 + +For secure tls (https) communication: -## Docker-Java maven dependencies + DOCKER_HOST=tcp://127.0.0.1:2376 + DOCKER_TLS_VERIFY=1 + DOCKER_CERT_PATH=/Users/marcus/.docker/machine/machines/docker-1.11.2 ### Latest release version -Supports a subset of the Docker Remote API [v1.19](https://github.com/docker/docker/blob/master/docs/reference/api/docker_remote_api_v1.19.md), Docker Server version 1.7.x - - - com.github.docker-java - docker-java - 2.2.3 - - -### Latest release candidate -Supports a subset of the Docker Remote API [v1.22](https://github.com/docker/docker/blob/master/docs/reference/api/docker_remote_api_v1.22.md), Docker Server version 1.10.x +Supports a subset of the Docker Remote API [v1.23](https://github.com/docker/docker/blob/master/docs/api/v1.23.md), Docker Server version 1.11.x com.github.docker-java docker-java - 3.0.0-RC5 + 3.0.6 ### Latest development version -Supports a subset of the Docker Remote API [v1.22](https://github.com/docker/docker/blob/master/docs/reference/api/docker_remote_api_v1.22.md), Docker Server version 1.10.x +Supports a subset of the Docker Remote API [v1.23](https://github.com/docker/docker/blob/master/docs/api/v1.23.md), Docker Server version 1.11.x You can find the latest development version including javadoc and source files on [Sonatypes OSS repository](https://oss.sonatype.org/content/groups/public/com/github/docker-java/docker-java/). com.github.docker-java docker-java - 3.0.0-SNAPSHOT + 3.0.7-SNAPSHOT @@ -99,7 +83,7 @@ There are a couple of configuration items, all of which have sensible defaults: * `DOCKER_TLS_VERIFY` enable/disable TLS verification (switch between `http` and `https` protocol) * `DOCKER_CERT_PATH` Path to the certificates needed for TLS verification * `DOCKER_CONFIG` Path for additional docker configuration files (like `.dockercfg`) -* `api.version` The API version, e.g. `1.21`. +* `api.version` The API version, e.g. `1.23`. * `registry.url` Your registry's address. * `registry.username` Your registry username (required to push containers). * `registry.password` Your registry password. @@ -110,12 +94,12 @@ There are three ways to configure, in descending order of precedence: #### Programmatic: In your application, e.g. - DockerClientConfig config = DockerClientConfig.createDefaultConfigBuilder() + DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() .withDockerHost("tcp://my-docker-host.tld:2376") .withDockerTlsVerify(true) .withDockerCertPath("/home/user/.docker/certs") .withDockerConfig("/home/user/.docker") - .withApiVersion("1.21") + .withApiVersion("1.23") .withRegistryUrl("https://index.docker.io/v1/") .withRegistryUsername("dockeruser") .withRegistryPassword("ilovedocker") @@ -129,7 +113,7 @@ In your application, e.g. DOCKER_TLS_VERIFY=1 DOCKER_CERT_PATH=/home/user/.docker/certs DOCKER_CONFIG=/home/user/.docker - api.version=1.21 + api.version=1.23 registry.url=https://index.docker.io/v1/ registry.username=dockeruser registry.password=ilovedocker @@ -137,11 +121,11 @@ In your application, e.g. ##### System Properties: - java -Dregistry.username=dockeruser pkg.Main + java -DDOCKER_HOST=tcp://localhost:2375 -Dregistry.username=dockeruser pkg.Main ##### System Environment - export DOCKER_URL=tcp://localhost:2376 + export DOCKER_HOST=tcp://localhost:2376 export DOCKER_TLS_VERIFY=1 export DOCKER_CERT_PATH=/home/user/.docker/certs export DOCKER_CONFIG=/home/user/.docker diff --git a/docs/devel.adoc b/docs/devel.adoc index 50c6a7279..1b6295662 100644 --- a/docs/devel.adoc +++ b/docs/devel.adoc @@ -13,6 +13,7 @@ ** Provide full information on field: *** For models define API version with `@since {@link RemoteApiVersion#VERSION_1_X}`. ** getters/setters should refernce to field `@see #$field`. + * If it is `Serializable` it shall have a `serialVersionUID` field. Unless code has shipped to users, the initial value of the `serialVersionUID` field shall be `1L`. ### Coding style * TBD, some initial styling already enforced with checkstyle. @@ -23,3 +24,11 @@ * Integration tests for commands. * If model object has builders, then fill it with data and compare by `equals()` with expected response from docker daemon. If failed, then some fields mappings are wrong. + +### Debug + * When there are unreproducible Travis errors: + ** Try locally run test 10-20 times in IDE against the same docker daemon version and same connection type (tcp or socket). + ** Limit `.travis.yml` to single run (to not consume their resources with matrix run). + ** Remove `travis-logback.xml` replacement (build can't output everything in every run because travis has log limitation). + ** Set single test in `pom.xml` `for maven-failsafe-plugin` + ** Make PR or if you are maintainer push to branch, catch log and fix. diff --git a/pom.xml b/pom.xml index 3b77e6e98..216c48631 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.github.docker-java docker-java jar - 3.0.0 + 3.0.7-SNAPSHOT docker-java https://github.com/docker-java/docker-java @@ -28,10 +28,20 @@ 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 - docker-java-3.0.0 + HEAD + + marcuslinke + Marcus Linke + marcus.linke@gmx.de + + + kostyasha + Kanstantsin Shautsou + kanstantsin.sha@gmail.com + kpelykh Konstantin Pelykh @@ -47,35 +57,35 @@ 1.7 1.7 - 2.11 + 2.23.1 2.6.4 - 4.3.1 - 1.5 - 1.8 - 2.3 + 4.5 + 1.12 + 1.10 + 2.5 2.6 - 1.7.5 + 1.7.21 - 1.51 - 2015-01-27T15-02-14 - 18.0 + 1.54 + 2.0.4 + 19.0 - 1.1.0 - 6.1.1 - 4.1.0.CR3 + 1.1.7 + 6.9.10 + 4.1.3.Final 1.3 - 1.6 + 1.8 2.3.3 1.10.19 - 2.2 - 2.3.1 - 2.3.1 + 3.0.2 + 3.5.1 + 2.5.3 2.19.1 2.19.1 - 1.7 + 1.8 @@ -89,6 +99,11 @@ jersey-apache-connector ${jersey.version} + + org.apache.httpcomponents + httpcore + 4.4.5 + org.apache.httpcomponents httpclient @@ -100,11 +115,15 @@ ${jersey.version} - de.gesellix - unix-socket-factory - ${unix-socket-factory.version} + com.kohlschutter.junixsocket + junixsocket-common + ${junixsocket.version} + + + com.kohlschutter.junixsocket + junixsocket-native-common + ${junixsocket.version} - org.apache.commons commons-compress @@ -133,7 +152,7 @@ org.slf4j jcl-over-slf4j - 1.7.12 + 1.7.21 @@ -206,7 +225,7 @@ com.google.code.findbugs annotations - 3.0.0 + 3.0.1 provided @@ -231,6 +250,12 @@ ${netty.version} linux-x86_64 + + junit + junit + 4.12 + test + @@ -245,13 +270,6 @@ - - - - - - - @@ -321,7 +339,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.0.1 attach-sources @@ -334,13 +352,16 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 2.10.4 attach-javadocs jar + + -Xdoclint:none + @@ -350,7 +371,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.2 + 1.6.7 true ossrh @@ -362,7 +383,7 @@ org.apache.maven.plugins maven-release-plugin - 2.5 + ${maven-release-plugin.version} true false @@ -376,7 +397,7 @@ maven-surefire-plugin ${maven-surefire-plugin.version} - integration + integration,integration-auth @@ -391,8 +412,11 @@ verify + true + true + 1 integration - + integration-auth **/*Test.java @@ -456,7 +480,8 @@ true true false - + src/test/resources/checkstyle/checkstyle-config.xml @@ -465,7 +490,7 @@ org.codehaus.mojo findbugs-maven-plugin - 3.0.2 + 3.0.3 Max Low @@ -484,20 +509,35 @@ org.jacoco jacoco-maven-plugin - 0.7.6.201602180812 + 0.7.7.201606060606 prepare-agent + - report + post-unit-test test report + + + pre-integration-test + pre-integration-test + + prepare-agent-integration + + + + report-integration + + report-integration + + diff --git a/src/main/java/com/github/dockerjava/api/DockerClient.java b/src/main/java/com/github/dockerjava/api/DockerClient.java index 4c0e2ed56..832890e87 100644 --- a/src/main/java/com/github/dockerjava/api/DockerClient.java +++ b/src/main/java/com/github/dockerjava/api/DockerClient.java @@ -35,6 +35,7 @@ import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.command.ListNetworksCmd; import com.github.dockerjava.api.command.ListVolumesCmd; +import com.github.dockerjava.api.command.LoadImageCmd; import com.github.dockerjava.api.command.LogContainerCmd; import com.github.dockerjava.api.command.PauseContainerCmd; import com.github.dockerjava.api.command.PingCmd; @@ -90,6 +91,18 @@ public interface DockerClient extends Closeable { CreateImageCmd createImageCmd(@Nonnull String repository, @Nonnull InputStream imageStream); + /** + * Loads a tarball with a set of images and tags into a Docker repository. + * + * Corresponds to POST /images/load API endpoint. + * + * @param imageStream + * stream of the tarball file + * @return created command + * @since {@link RemoteApiVersion#VERSION_1_7} + */ + LoadImageCmd loadImageCmd(@Nonnull InputStream imageStream); + SearchImagesCmd searchImagesCmd(@Nonnull String term); RemoveImageCmd removeImageCmd(@Nonnull String imageId); @@ -157,6 +170,7 @@ public interface DockerClient extends Closeable { * @return created command * @see #copyArchiveFromContainerCmd(String, String) * @deprecated since docker API version 1.20, replaced by {@link #copyArchiveFromContainerCmd(String, String)} + * since 1.24 fails. */ @Deprecated CopyFileFromContainerCmd copyFileFromContainerCmd(@Nonnull String containerId, @Nonnull String resource); diff --git a/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java b/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java index 20b23e903..ac98db2f9 100644 --- a/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/BuildImageCmd.java @@ -101,6 +101,12 @@ public interface BuildImageCmd extends AsyncDockerCmd getLabels(); + // setters BuildImageCmd withTag(String tag); @@ -145,6 +151,11 @@ public interface BuildImageCmd extends AsyncDockerCmd labels); + interface Exec extends DockerCmdAsyncExec { } diff --git a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java index 65bdafcac..e2b0072fa 100644 --- a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java @@ -6,6 +6,7 @@ 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.LogConfig; import com.github.dockerjava.api.model.LxcConf; @@ -187,6 +188,9 @@ public interface CreateContainerCmd extends SyncDockerCmd securityOpt); diff --git a/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java b/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java index ae090e434..671c0d535 100644 --- a/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java +++ b/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java @@ -3,8 +3,6 @@ import java.io.Closeable; import java.io.IOException; -import javax.net.ssl.SSLContext; - import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.RemoteApiVersion; @@ -30,6 +28,8 @@ public interface DockerCmdExecFactory extends Closeable { CreateImageCmd.Exec createCreateImageCmdExec(); + LoadImageCmd.Exec createLoadImageCmdExec(); + SearchImagesCmd.Exec createSearchImagesCmdExec(); RemoveImageCmd.Exec createRemoveImageCmdExec(); @@ -117,8 +117,6 @@ public interface DockerCmdExecFactory extends Closeable { DisconnectFromNetworkCmd.Exec createDisconnectFromNetworkCmdExec(); - DockerCmdExecFactory withSSLContext(SSLContext sslContext); - @Override void close() throws IOException; diff --git a/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java b/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java index fe2329290..829ebf8ee 100644 --- a/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/ExecCreateCmd.java @@ -20,6 +20,9 @@ public interface ExecCreateCmd extends SyncDockerCmd { @CheckForNull Boolean hasTtyEnabled(); + @CheckForNull + String getUser(); + ExecCreateCmd withAttachStderr(Boolean attachStderr); ExecCreateCmd withAttachStdin(Boolean attachStdin); @@ -32,6 +35,8 @@ public interface ExecCreateCmd extends SyncDockerCmd { ExecCreateCmd withTty(Boolean tty); + ExecCreateCmd withUser(String user); + interface Exec extends DockerCmdSyncExec { } diff --git a/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java b/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java index 59a267e9d..c8027b0ed 100644 --- a/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java +++ b/src/main/java/com/github/dockerjava/api/command/InspectContainerResponse.java @@ -53,6 +53,12 @@ public class InspectContainerResponse { @JsonProperty("HostsPath") private String hostsPath; + /** + * @since {@link RemoteApiVersion#VERSION_1_17} + */ + @JsonProperty("LogPath") + private String logPath; + @JsonProperty("Id") private String id; @@ -168,6 +174,11 @@ public String getHostsPath() { return hostsPath; } + @CheckForNull + public String getLogPath() { + return logPath; + } + public String getName() { return name; } diff --git a/src/main/java/com/github/dockerjava/api/command/LoadImageCmd.java b/src/main/java/com/github/dockerjava/api/command/LoadImageCmd.java new file mode 100644 index 000000000..184b7ef33 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/LoadImageCmd.java @@ -0,0 +1,21 @@ +package com.github.dockerjava.api.command; + +import java.io.InputStream; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +public interface LoadImageCmd extends SyncDockerCmd { + + @CheckForNull + InputStream getImageStream(); + + /** + * @param imageStream + * the InputStream of the tar file + */ + LoadImageCmd withImageStream(@Nonnull InputStream imageStream); + + interface Exec extends DockerCmdSyncExec { + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/AuthConfig.java b/src/main/java/com/github/dockerjava/api/model/AuthConfig.java index 849f26a21..074c6db61 100644 --- a/src/main/java/com/github/dockerjava/api/model/AuthConfig.java +++ b/src/main/java/com/github/dockerjava/api/model/AuthConfig.java @@ -8,9 +8,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import javax.annotation.CheckForNull; +import java.io.Serializable; @JsonInclude(Include.NON_NULL) -public class AuthConfig { +public class AuthConfig implements Serializable { + private static final long serialVersionUID = 1L; /** * For backwards compatibility. Make sure you update the properties if you change this. diff --git a/src/main/java/com/github/dockerjava/api/model/AuthConfigurations.java b/src/main/java/com/github/dockerjava/api/model/AuthConfigurations.java index e8edbc950..baa8fc0b4 100644 --- a/src/main/java/com/github/dockerjava/api/model/AuthConfigurations.java +++ b/src/main/java/com/github/dockerjava/api/model/AuthConfigurations.java @@ -1,11 +1,13 @@ package com.github.dockerjava.api.model; +import java.io.Serializable; import java.util.Map; import java.util.TreeMap; import com.fasterxml.jackson.annotation.JsonProperty; -public class AuthConfigurations { +public class AuthConfigurations implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("configs") private Map configs = new TreeMap<>(); diff --git a/src/main/java/com/github/dockerjava/api/model/AuthResponse.java b/src/main/java/com/github/dockerjava/api/model/AuthResponse.java index cb5d9df80..cd6908185 100644 --- a/src/main/java/com/github/dockerjava/api/model/AuthResponse.java +++ b/src/main/java/com/github/dockerjava/api/model/AuthResponse.java @@ -2,7 +2,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; -public class AuthResponse { +import java.io.Serializable; + +public class AuthResponse implements Serializable { + private static final long serialVersionUID = 1L; + @JsonProperty("Status") private String 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 index bf02bbad2..9a7ebf18d 100644 --- a/src/main/java/com/github/dockerjava/api/model/Bind.java +++ b/src/main/java/com/github/dockerjava/api/model/Bind.java @@ -3,11 +3,14 @@ import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; +import java.io.Serializable; + /** * 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 { +public class Bind implements Serializable { + private static final long serialVersionUID = 1L; private String path; @@ -15,24 +18,48 @@ public class Bind { private AccessMode accessMode; + /** + * @since {@link com.github.dockerjava.core.RemoteApiVersion#VERSION_1_23} + */ + private Boolean noCopy; + /** * @since {@link com.github.dockerjava.core.RemoteApiVersion#VERSION_1_17} */ private SELContext secMode; + /** + * @since {@link com.github.dockerjava.core.RemoteApiVersion#VERSION_1_22} + */ + private PropagationMode propagationMode; + public Bind(String path, Volume volume) { this(path, volume, AccessMode.DEFAULT, SELContext.DEFAULT); } + public Bind(String path, Volume volume, Boolean noCopy) { + this(path, volume, AccessMode.DEFAULT, SELContext.DEFAULT, noCopy); + } + public Bind(String path, Volume volume, AccessMode accessMode) { this(path, volume, accessMode, SELContext.DEFAULT); } public Bind(String path, Volume volume, AccessMode accessMode, SELContext secMode) { + this(path, volume, accessMode, secMode, null); + } + + public Bind(String path, Volume volume, AccessMode accessMode, SELContext secMode, Boolean noCopy) { + this(path, volume, accessMode, secMode, noCopy, PropagationMode.DEFAULT_MODE); + } + + public Bind(String path, Volume volume, AccessMode accessMode, SELContext secMode, Boolean noCopy, PropagationMode propagationMode) { this.path = path; this.volume = volume; this.accessMode = accessMode; this.secMode = secMode; + this.noCopy = noCopy; + this.propagationMode = propagationMode; } public String getPath() { @@ -51,6 +78,14 @@ public SELContext getSecMode() { return secMode; } + public Boolean getNoCopy() { + return noCopy; + } + + public PropagationMode getPropagationMode() { + return propagationMode; + } + /** * Parses a bind mount specification to a {@link Bind}. * @@ -71,15 +106,25 @@ public static Bind parse(String serialized) { String[] flags = parts[2].split(","); AccessMode accessMode = AccessMode.DEFAULT; SELContext seMode = SELContext.DEFAULT; + Boolean nocopy = null; + PropagationMode propagationMode = PropagationMode.DEFAULT_MODE; for (String p : flags) { if (p.length() == 2) { accessMode = AccessMode.valueOf(p.toLowerCase()); + } else if ("nocopy".equals(p)) { + nocopy = true; + } else if (PropagationMode.SHARED.toString().equals(p)) { + propagationMode = PropagationMode.SHARED; + } else if (PropagationMode.SLAVE.toString().equals(p)) { + propagationMode = PropagationMode.SLAVE; + } else if (PropagationMode.PRIVATE.toString().equals(p)) { + propagationMode = PropagationMode.PRIVATE; } else { seMode = SELContext.fromString(p); } } - return new Bind(parts[0], new Volume(parts[1]), accessMode, seMode); + return new Bind(parts[0], new Volume(parts[1]), accessMode, seMode, nocopy, propagationMode); } default: { throw new IllegalArgumentException(); @@ -99,6 +144,8 @@ public boolean equals(Object obj) { .append(volume, other.getVolume()) .append(accessMode, other.getAccessMode()) .append(secMode, other.getSecMode()) + .append(noCopy, other.getNoCopy()) + .append(propagationMode, other.getPropagationMode()) .isEquals(); } else { return super.equals(obj); @@ -112,6 +159,8 @@ public int hashCode() { .append(volume) .append(accessMode) .append(secMode) + .append(noCopy) + .append(propagationMode) .toHashCode(); } @@ -124,10 +173,12 @@ public int hashCode() { */ @Override public String toString() { - return String.format("%s:%s:%s%s", + return String.format("%s:%s:%s%s%s%s", path, volume.getPath(), accessMode.toString(), - secMode != SELContext.none ? "," + secMode.toString() : ""); + secMode != SELContext.none ? "," + secMode.toString() : "", + noCopy != null ? ",nocopy" : "", + propagationMode != PropagationMode.DEFAULT_MODE ? "," + propagationMode.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 index 917cfdcbd..b24c3208d 100644 --- a/src/main/java/com/github/dockerjava/api/model/Binds.java +++ b/src/main/java/com/github/dockerjava/api/model/Binds.java @@ -1,6 +1,7 @@ package com.github.dockerjava.api.model; import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -19,7 +20,8 @@ @JsonSerialize(using = Binds.Serializer.class) @JsonDeserialize(using = Binds.Deserializer.class) -public class Binds { +public class Binds implements Serializable { + private static final long serialVersionUID = 1L; private Bind[] binds; diff --git a/src/main/java/com/github/dockerjava/api/model/BuildResponseItem.java b/src/main/java/com/github/dockerjava/api/model/BuildResponseItem.java index a53a1df9a..7236be23d 100644 --- a/src/main/java/com/github/dockerjava/api/model/BuildResponseItem.java +++ b/src/main/java/com/github/dockerjava/api/model/BuildResponseItem.java @@ -8,7 +8,6 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) public class BuildResponseItem extends ResponseItem { - private static final long serialVersionUID = -1252904184236343612L; private static final String BUILD_SUCCESS = "Successfully built"; diff --git a/src/main/java/com/github/dockerjava/api/model/ChangeLog.java b/src/main/java/com/github/dockerjava/api/model/ChangeLog.java index 36804362c..04b36f5ce 100644 --- a/src/main/java/com/github/dockerjava/api/model/ChangeLog.java +++ b/src/main/java/com/github/dockerjava/api/model/ChangeLog.java @@ -5,13 +5,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + /** * * @author Konstantin Pelykh (kpelykh@gmail.com) * */ @JsonIgnoreProperties(ignoreUnknown = true) -public class ChangeLog { +public class ChangeLog implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("Path") private String path; diff --git a/src/main/java/com/github/dockerjava/api/model/Container.java b/src/main/java/com/github/dockerjava/api/model/Container.java index 47492920d..9e04362ce 100644 --- a/src/main/java/com/github/dockerjava/api/model/Container.java +++ b/src/main/java/com/github/dockerjava/api/model/Container.java @@ -11,6 +11,7 @@ import org.apache.commons.lang.builder.ToStringBuilder; import javax.annotation.CheckForNull; +import java.io.Serializable; import java.util.Map; /** @@ -20,7 +21,8 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(Include.NON_NULL) -public class Container { +public class Container implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("Command") private String command; diff --git a/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java b/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java index 224da3272..191126798 100644 --- a/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java +++ b/src/main/java/com/github/dockerjava/api/model/ContainerConfig.java @@ -10,6 +10,7 @@ import org.apache.commons.lang.builder.ToStringBuilder; import javax.annotation.CheckForNull; +import java.io.Serializable; import java.util.Map; /** @@ -19,7 +20,8 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(Include.NON_NULL) -public class ContainerConfig { +public class ContainerConfig implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("AttachStderr") private Boolean attachStderr; diff --git a/src/main/java/com/github/dockerjava/api/model/ContainerHostConfig.java b/src/main/java/com/github/dockerjava/api/model/ContainerHostConfig.java index 43a5a94b1..8d0c3fe37 100644 --- a/src/main/java/com/github/dockerjava/api/model/ContainerHostConfig.java +++ b/src/main/java/com/github/dockerjava/api/model/ContainerHostConfig.java @@ -6,6 +6,8 @@ import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; +import java.io.Serializable; + /** * Used in {@link Container} * @@ -13,7 +15,9 @@ * @author Kanstantsin Shautsou */ @JsonIgnoreProperties(ignoreUnknown = true) -public class ContainerHostConfig { +public class ContainerHostConfig implements Serializable { + private static final long serialVersionUID = 1L; + @JsonProperty("NetworkMode") private String networkMode; diff --git a/src/main/java/com/github/dockerjava/api/model/ContainerNetwork.java b/src/main/java/com/github/dockerjava/api/model/ContainerNetwork.java index 9b2160e98..bc7e12d33 100644 --- a/src/main/java/com/github/dockerjava/api/model/ContainerNetwork.java +++ b/src/main/java/com/github/dockerjava/api/model/ContainerNetwork.java @@ -9,6 +9,7 @@ import org.apache.commons.lang.builder.ToStringBuilder; import javax.annotation.CheckForNull; +import java.io.Serializable; import java.util.Arrays; import java.util.List; @@ -21,7 +22,9 @@ * @author Kanstantsin Shautsou */ @JsonIgnoreProperties(ignoreUnknown = true) -public class ContainerNetwork { +public class ContainerNetwork implements Serializable { + private static final long serialVersionUID = 1L; + /** * FIXME verify */ diff --git a/src/main/java/com/github/dockerjava/api/model/ContainerNetworkSettings.java b/src/main/java/com/github/dockerjava/api/model/ContainerNetworkSettings.java index 7f4b17be5..bc6165874 100644 --- a/src/main/java/com/github/dockerjava/api/model/ContainerNetworkSettings.java +++ b/src/main/java/com/github/dockerjava/api/model/ContainerNetworkSettings.java @@ -7,6 +7,7 @@ import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; +import java.io.Serializable; import java.util.Map; /** @@ -16,7 +17,9 @@ * @since {@link RemoteApiVersion#VERSION_1_22} */ @JsonIgnoreProperties(ignoreUnknown = true) -public class ContainerNetworkSettings { +public class ContainerNetworkSettings implements Serializable { + private static final long serialVersionUID = 1L; + /** * @since {@link RemoteApiVersion#VERSION_1_22} */ diff --git a/src/main/java/com/github/dockerjava/api/model/ContainerPort.java b/src/main/java/com/github/dockerjava/api/model/ContainerPort.java index 09f718ef6..5924e53df 100644 --- a/src/main/java/com/github/dockerjava/api/model/ContainerPort.java +++ b/src/main/java/com/github/dockerjava/api/model/ContainerPort.java @@ -7,13 +7,15 @@ import org.apache.commons.lang.builder.ToStringBuilder; import javax.annotation.CheckForNull; +import java.io.Serializable; /** * @author Kanstantsin Shautsou * @see Container */ @JsonIgnoreProperties(ignoreUnknown = true) -public class ContainerPort { +public class ContainerPort implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("IP") private String ip; diff --git a/src/main/java/com/github/dockerjava/api/model/Device.java b/src/main/java/com/github/dockerjava/api/model/Device.java index 9240239b1..7d73710fd 100644 --- a/src/main/java/com/github/dockerjava/api/model/Device.java +++ b/src/main/java/com/github/dockerjava/api/model/Device.java @@ -1,6 +1,8 @@ package com.github.dockerjava.api.model; import static com.google.common.base.Preconditions.checkNotNull; +import static org.apache.commons.lang.BooleanUtils.isNotTrue; +import static org.apache.commons.lang.StringUtils.isEmpty; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; @@ -9,8 +11,15 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.Nonnull; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + @JsonInclude(Include.NON_NULL) -public class Device { +public class Device implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("CgroupPermissions") private String cGroupPermissions = ""; @@ -45,6 +54,75 @@ public String getPathOnHost() { return pathOnHost; } + /** + * @link https://github.com/docker/docker/blob/6b4a46f28266031ce1a1315f17fb69113a06efe1/runconfig/opts/parse_test.go#L468 + */ + @Nonnull + public static Device parse(@Nonnull String deviceStr) { + String src = ""; + String dst = ""; + String permissions = "rwm"; + final String[] arr = deviceStr.trim().split(":"); + // java String.split() returns wrong length, use tokenizer instead + switch (new StringTokenizer(deviceStr, ":").countTokens()) { + case 3: { + // Mismatches docker code logic. While there is no validations after parsing, checking heregit + if (validDeviceMode(arr[2])) { + permissions = arr[2]; + } else { + throw new IllegalArgumentException("Invalid device specification: " + deviceStr); + } + } + case 2: { + if (validDeviceMode(arr[1])) { + permissions = arr[1]; + } else { + dst = arr[1]; + } + } + case 1: { + src = arr[0]; + break; + } + default: { + throw new IllegalArgumentException("Invalid device specification: " + deviceStr); + } + } + + if (isEmpty(dst)) { + dst = src; + } + + return new Device(permissions, dst, src); + } + + /** + * ValidDeviceMode checks if the mode for device is valid or not. + * Valid mode is a composition of r (read), w (write), and m (mknod). + * + * @link https://github.com/docker/docker/blob/6b4a46f28266031ce1a1315f17fb69113a06efe1/runconfig/opts/parse.go#L796 + */ + private static boolean validDeviceMode(String deviceMode) { + Map validModes = new HashMap<>(3); + validModes.put("r", true); + validModes.put("w", true); + validModes.put("m", true); + + if (isEmpty(deviceMode)) { + return false; + } + + for (char ch : deviceMode.toCharArray()) { + final String mode = String.valueOf(ch); + if (isNotTrue(validModes.get(mode))) { + return false; // wrong mode + } + validModes.put(mode, false); + } + + return true; + } + @Override public boolean equals(Object obj) { if (obj instanceof Device) { diff --git a/src/main/java/com/github/dockerjava/api/model/DriverStatus.java b/src/main/java/com/github/dockerjava/api/model/DriverStatus.java index 3e2b5037a..7c1374aae 100644 --- a/src/main/java/com/github/dockerjava/api/model/DriverStatus.java +++ b/src/main/java/com/github/dockerjava/api/model/DriverStatus.java @@ -7,12 +7,15 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + /** - * Created by ben on 12/12/13. + * @author ben */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(Include.NON_NULL) -public class DriverStatus { +public class DriverStatus implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("Root Dir") private String rootDir; diff --git a/src/main/java/com/github/dockerjava/api/model/ErrorDetail.java b/src/main/java/com/github/dockerjava/api/model/ErrorDetail.java index 62018a4e0..92e869d2a 100644 --- a/src/main/java/com/github/dockerjava/api/model/ErrorDetail.java +++ b/src/main/java/com/github/dockerjava/api/model/ErrorDetail.java @@ -4,8 +4,12 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + @JsonInclude(Include.NON_NULL) -public class ErrorDetail { +public class ErrorDetail implements Serializable { + private static final long serialVersionUID = 1L; + @JsonProperty private String 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 index 172bd5c79..f6abfc3eb 100644 --- a/src/main/java/com/github/dockerjava/api/model/ErrorResponse.java +++ b/src/main/java/com/github/dockerjava/api/model/ErrorResponse.java @@ -4,8 +4,12 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + @JsonInclude(Include.NON_NULL) -public class ErrorResponse { +public class ErrorResponse implements Serializable { + private static final long serialVersionUID = 1L; + @JsonProperty private ErrorDetail errorDetail; diff --git a/src/main/java/com/github/dockerjava/api/model/Event.java b/src/main/java/com/github/dockerjava/api/model/Event.java index 202f5a350..6e4b6f3c2 100644 --- a/src/main/java/com/github/dockerjava/api/model/Event.java +++ b/src/main/java/com/github/dockerjava/api/model/Event.java @@ -1,27 +1,81 @@ package com.github.dockerjava.api.model; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; 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 javax.annotation.CheckForNull; + +import static org.apache.commons.lang.builder.ToStringStyle.SHORT_PREFIX_STYLE; + +import java.io.Serializable; /** * Representation of a Docker event. */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(Include.NON_NULL) -public class Event { +public class Event implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * @since 1.16 + */ + @JsonProperty("status") private String status; + /** + * @since 1.16 + */ + @JsonProperty("id") private String id; + /** + * @since 1.16 + */ + @JsonProperty("from") private String from; + /** + * ?? + */ + @JsonProperty("node") + private Node node; + + /* + * @since 1. + */ + @JsonProperty("Type") + private EventType type; + + /** + * @since 1.22 + */ + @JsonProperty("Action") + private String action; + + /** + * @since 1.22 + */ + @JsonProperty("Actor") + private EventActor actor; + + /** + * @since 1.16 + */ + @JsonProperty("time") private Long time; - @JsonIgnoreProperties - private Node node; + /** + * @since 1.21 + */ + @JsonProperty("timeNano") + private Long timeNano; /** * Default constructor for the deserialization. @@ -32,16 +86,12 @@ 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 + * @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 1.16 */ public Event(String status, String id, String from, Long time) { this.status = status; @@ -60,6 +110,14 @@ public String getStatus() { return status; } + /** + * @see #status + */ + public Event withStatus(String status) { + this.status = status; + return this; + } + /** * Get ID of docker container. * @@ -69,6 +127,14 @@ public String getId() { return id; } + /** + * @see #id + */ + public Event withId(String id) { + this.id = id; + return this; + } + /** * Get source image of the container. * @@ -78,6 +144,14 @@ public String getFrom() { return from; } + /** + * @see #from + */ + public Event withFrom(String from) { + this.from = from; + return this; + } + /** * Get the event time. The time is specified in milliseconds since January 1, 1970, 00:00:00 GMT * @@ -87,6 +161,30 @@ public Long getTime() { return time; } + /** + * @see #time + */ + public Event withTime(Long time) { + this.time = time; + return this; + } + + /** + * @see #timeNano + */ + @CheckForNull + public Long getTimeNano() { + return timeNano; + } + + /** + * @see #timeNano + */ + public Event withTimenano(Long timenano) { + this.timeNano = timenano; + return this; + } + /** * Returns the node when working against docker swarm */ @@ -94,8 +192,71 @@ public Node getNode() { return node; } + /** + * @see #node + */ + public Event withNode(Node node) { + this.node = node; + return this; + } + + @CheckForNull + public EventType getType() { + return type; + } + + /** + * @see #type + */ + public Event withType(EventType type) { + this.type = type; + return this; + } + + /** + * @see #action + */ + @CheckForNull + public String getAction() { + return action; + } + + /** + * @see #action + */ + public Event withAction(String action) { + this.action = action; + return this; + } + + /** + * @see #actor + */ + @CheckForNull + public EventActor getActor() { + return actor; + } + + /** + * @see #actor + */ + public Event withEventActor(EventActor actor) { + this.actor = actor; + return this; + } + @Override public String toString() { - return ToStringBuilder.reflectionToString(this); + return ToStringBuilder.reflectionToString(this, SHORT_PREFIX_STYLE); + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); } } diff --git a/src/main/java/com/github/dockerjava/api/model/EventActor.java b/src/main/java/com/github/dockerjava/api/model/EventActor.java new file mode 100644 index 000000000..f7b0abbf3 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/EventActor.java @@ -0,0 +1,77 @@ +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 org.apache.commons.lang.builder.ToStringBuilder; + +import javax.annotation.CheckForNull; +import java.io.Serializable; +import java.util.Map; + +/** + * @author Kanstantsin Shautsou + * @since 1.22 + */ +public class EventActor implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * @since 1.22 + */ + @JsonProperty("ID") + private String id; + + /** + * @since 1.22 + */ + @JsonProperty("Attributes") + private Map attributes; + + /** + * @see #id + */ + @CheckForNull + public String getId() { + return id; + } + + /** + * @see #id + */ + public EventActor withId(String id) { + this.id = id; + return this; + } + + /** + * @see #attributes + */ + @CheckForNull + public Map getAttributes() { + return attributes; + } + + /** + * @see #attributes + */ + public EventActor withAttributes(Map attributes) { + this.attributes = attributes; + return this; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/EventType.java b/src/main/java/com/github/dockerjava/api/model/EventType.java new file mode 100644 index 000000000..697c1e429 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/EventType.java @@ -0,0 +1,55 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import javax.annotation.Nonnull; +import java.util.HashMap; +import java.util.Map; + +/** + * @since 1.24 + */ +public enum EventType { + /** + * @since 1.24 + */ + CONTAINER("container"), + + /** + * @since 1.24 + */ + DAEMON("daemon"), + + /** + * @since 1.24 + */ + IMAGE("image"), + NETWORK("network"), + PLUGIN("plugin"), + VOLUME("volume"); + + private static final Map EVENT_TYPES = new HashMap<>(); + + static { + for (EventType t : values()) { + EVENT_TYPES.put(t.name().toLowerCase(), t); + } + } + + private String value; + + EventType(@Nonnull String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @JsonCreator + public static EventType forValue(String s) { + return EVENT_TYPES.get(s); + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/ExposedPort.java b/src/main/java/com/github/dockerjava/api/model/ExposedPort.java index ec95f6273..26d727ff4 100644 --- a/src/main/java/com/github/dockerjava/api/model/ExposedPort.java +++ b/src/main/java/com/github/dockerjava/api/model/ExposedPort.java @@ -4,6 +4,7 @@ import static com.github.dockerjava.api.model.InternetProtocol.UDP; import java.io.IOException; +import java.io.Serializable; import java.util.Map.Entry; import org.apache.commons.lang.builder.EqualsBuilder; @@ -30,7 +31,8 @@ */ @JsonDeserialize(using = ExposedPort.Deserializer.class) @JsonSerialize(using = ExposedPort.Serializer.class) -public class ExposedPort { +public class ExposedPort implements Serializable { + private static final long serialVersionUID = 1L; private final InternetProtocol protocol; diff --git a/src/main/java/com/github/dockerjava/api/model/ExposedPorts.java b/src/main/java/com/github/dockerjava/api/model/ExposedPorts.java index 0ecb879b6..7670212e1 100644 --- a/src/main/java/com/github/dockerjava/api/model/ExposedPorts.java +++ b/src/main/java/com/github/dockerjava/api/model/ExposedPorts.java @@ -1,6 +1,7 @@ package com.github.dockerjava.api.model; import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -21,7 +22,8 @@ @JsonSerialize(using = ExposedPorts.Serializer.class) @JsonDeserialize(using = ExposedPorts.Deserializer.class) -public class ExposedPorts { +public class ExposedPorts implements Serializable { + private static final long serialVersionUID = 1L; private ExposedPort[] exposedPorts; diff --git a/src/main/java/com/github/dockerjava/api/model/Frame.java b/src/main/java/com/github/dockerjava/api/model/Frame.java index 0ad16e8c6..9b1376f82 100644 --- a/src/main/java/com/github/dockerjava/api/model/Frame.java +++ b/src/main/java/com/github/dockerjava/api/model/Frame.java @@ -1,11 +1,14 @@ package com.github.dockerjava.api.model; +import java.io.Serializable; import java.util.Arrays; /** * Represents a logging frame. */ -public class Frame { +public class Frame implements Serializable { + private static final long serialVersionUID = 1L; + private final StreamType streamType; private final byte[] payload; diff --git a/src/main/java/com/github/dockerjava/api/model/HostConfig.java b/src/main/java/com/github/dockerjava/api/model/HostConfig.java index 4d9d7f145..62bde8650 100644 --- a/src/main/java/com/github/dockerjava/api/model/HostConfig.java +++ b/src/main/java/com/github/dockerjava/api/model/HostConfig.java @@ -11,6 +11,7 @@ import org.apache.commons.lang.builder.ToStringBuilder; import javax.annotation.CheckForNull; +import java.io.Serializable; import java.util.Arrays; import java.util.List; @@ -20,7 +21,8 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(Include.NON_NULL) -public class HostConfig { +public class HostConfig implements Serializable { + private static final long serialVersionUID = 1L; private static final List PREDEFINED_NETWORKS = Arrays.asList("bridge", "host", "none"); @@ -190,7 +192,7 @@ public class HostConfig { * @since {@link RemoteApiVersion#VERSION_1_22} */ @JsonProperty("ShmSize") - private String shmSize; + private Long shmSize; @JsonIgnore @@ -401,7 +403,7 @@ public String getCgroupParent() { * @see #shmSize */ @CheckForNull - public String getShmSize() { + public Long getShmSize() { return shmSize; } @@ -764,7 +766,7 @@ public HostConfig withSecurityOpts(String[] securityOpts) { /** * @see #shmSize */ - public HostConfig withShmSize(String shmSize) { + public HostConfig withShmSize(Long shmSize) { this.shmSize = shmSize; return 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 index 1051d857f..8f1c871a7 100644 --- a/src/main/java/com/github/dockerjava/api/model/Identifier.java +++ b/src/main/java/com/github/dockerjava/api/model/Identifier.java @@ -3,10 +3,14 @@ import com.google.common.base.Objects; import com.google.common.base.Optional; +import java.io.Serializable; + /** - * Created by magnayn on 22/07/2014. + * @author magnayn */ -public class Identifier { +public class Identifier implements Serializable { + private static final long serialVersionUID = 1L; + public final Repository repository; public final Optional tag; diff --git a/src/main/java/com/github/dockerjava/api/model/Image.java b/src/main/java/com/github/dockerjava/api/model/Image.java index 496db275e..78fee9f79 100644 --- a/src/main/java/com/github/dockerjava/api/model/Image.java +++ b/src/main/java/com/github/dockerjava/api/model/Image.java @@ -7,6 +7,8 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + /** * * @author Konstantin Pelykh (kpelykh@gmail.com) @@ -14,7 +16,8 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(Include.NON_NULL) -public class Image { +public class Image implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("Created") private Long created; diff --git a/src/main/java/com/github/dockerjava/api/model/Info.java b/src/main/java/com/github/dockerjava/api/model/Info.java index 7270eceb8..51abe5dac 100644 --- a/src/main/java/com/github/dockerjava/api/model/Info.java +++ b/src/main/java/com/github/dockerjava/api/model/Info.java @@ -9,6 +9,7 @@ import org.apache.commons.lang.builder.ToStringBuilder; import javax.annotation.CheckForNull; +import java.io.Serializable; import java.util.List; import java.util.Map; @@ -19,7 +20,8 @@ */ @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public class Info { +public class Info implements Serializable { + private static final long serialVersionUID = 1L; /** * @since {@link com.github.dockerjava.core.RemoteApiVersion#VERSION_1_22} diff --git a/src/main/java/com/github/dockerjava/api/model/InfoRegistryConfig.java b/src/main/java/com/github/dockerjava/api/model/InfoRegistryConfig.java index 956aaebe3..2c2be5cfd 100644 --- a/src/main/java/com/github/dockerjava/api/model/InfoRegistryConfig.java +++ b/src/main/java/com/github/dockerjava/api/model/InfoRegistryConfig.java @@ -7,6 +7,7 @@ import org.apache.commons.lang.builder.ToStringBuilder; import javax.annotation.CheckForNull; +import java.io.Serializable; import java.util.List; import java.util.Map; @@ -14,7 +15,9 @@ * @since ~{@link com.github.dockerjava.core.RemoteApiVersion#VERSION_1_20} */ @JsonIgnoreProperties(ignoreUnknown = true) -public final class InfoRegistryConfig { +public final class InfoRegistryConfig implements Serializable { + private static final long serialVersionUID = 1L; + @JsonProperty("IndexConfigs") private Map indexConfigs; diff --git a/src/main/java/com/github/dockerjava/api/model/Link.java b/src/main/java/com/github/dockerjava/api/model/Link.java index 89647306c..346aedcb0 100644 --- a/src/main/java/com/github/dockerjava/api/model/Link.java +++ b/src/main/java/com/github/dockerjava/api/model/Link.java @@ -3,12 +3,15 @@ import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; +import java.io.Serializable; + /** * 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 { +public class Link implements Serializable { + private static final long serialVersionUID = 1L; private final String name; diff --git a/src/main/java/com/github/dockerjava/api/model/Links.java b/src/main/java/com/github/dockerjava/api/model/Links.java index 2d678b3a3..1eb66ae42 100644 --- a/src/main/java/com/github/dockerjava/api/model/Links.java +++ b/src/main/java/com/github/dockerjava/api/model/Links.java @@ -1,6 +1,7 @@ package com.github.dockerjava.api.model; import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -20,7 +21,8 @@ @JsonSerialize(using = Links.Serializer.class) @JsonDeserialize(using = Links.Deserializer.class) -public class Links { +public class Links implements Serializable { + private static final long serialVersionUID = 1L; private final Link[] links; diff --git a/src/main/java/com/github/dockerjava/api/model/LogConfig.java b/src/main/java/com/github/dockerjava/api/model/LogConfig.java index f4fd0e958..e2a066101 100644 --- a/src/main/java/com/github/dockerjava/api/model/LogConfig.java +++ b/src/main/java/com/github/dockerjava/api/model/LogConfig.java @@ -1,6 +1,7 @@ package com.github.dockerjava.api.model; import java.io.IOException; +import java.io.Serializable; import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -26,7 +27,8 @@ * docker will ignore them. In most cases setting the config option to null will suffice. Consult the docker remote API for a more detailed * and up-to-date explanation of the available types and their options. */ -public class LogConfig { +public class LogConfig implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("Type") public LoggingType type = null; diff --git a/src/main/java/com/github/dockerjava/api/model/LxcConf.java b/src/main/java/com/github/dockerjava/api/model/LxcConf.java index aeb854285..0a60086fb 100644 --- a/src/main/java/com/github/dockerjava/api/model/LxcConf.java +++ b/src/main/java/com/github/dockerjava/api/model/LxcConf.java @@ -4,8 +4,12 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + @JsonInclude(Include.NON_NULL) -public class LxcConf { +public class LxcConf implements Serializable { + private static final long serialVersionUID = 1L; + @JsonProperty("Key") public String key; diff --git a/src/main/java/com/github/dockerjava/api/model/Network.java b/src/main/java/com/github/dockerjava/api/model/Network.java index 6f4114914..c0f9f36eb 100644 --- a/src/main/java/com/github/dockerjava/api/model/Network.java +++ b/src/main/java/com/github/dockerjava/api/model/Network.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.lang.builder.ToStringBuilder; +import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -12,7 +13,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) -public class Network { +public class Network implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("Id") private String id; diff --git a/src/main/java/com/github/dockerjava/api/model/NetworkSettings.java b/src/main/java/com/github/dockerjava/api/model/NetworkSettings.java index 1d523bf4b..8fcd8fb9b 100644 --- a/src/main/java/com/github/dockerjava/api/model/NetworkSettings.java +++ b/src/main/java/com/github/dockerjava/api/model/NetworkSettings.java @@ -8,6 +8,7 @@ import com.github.dockerjava.core.RemoteApiVersion; import org.apache.commons.lang.builder.ToStringBuilder; +import java.io.Serializable; import java.util.Map; /** @@ -16,7 +17,8 @@ * */ @JsonIgnoreProperties(ignoreUnknown = true) -public class NetworkSettings { +public class NetworkSettings implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("Bridge") private String bridge; diff --git a/src/main/java/com/github/dockerjava/api/model/Node.java b/src/main/java/com/github/dockerjava/api/model/Node.java index 9e8580e0a..89b391c0a 100644 --- a/src/main/java/com/github/dockerjava/api/model/Node.java +++ b/src/main/java/com/github/dockerjava/api/model/Node.java @@ -4,11 +4,14 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + /** * A node as returned by the /events API, for instance, when Swarm is used. */ @JsonInclude(Include.NON_NULL) -public class Node { +public class Node implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("Name") private String name; diff --git a/src/main/java/com/github/dockerjava/api/model/PortBinding.java b/src/main/java/com/github/dockerjava/api/model/PortBinding.java index e163272d7..9a30b6cd2 100644 --- a/src/main/java/com/github/dockerjava/api/model/PortBinding.java +++ b/src/main/java/com/github/dockerjava/api/model/PortBinding.java @@ -6,6 +6,8 @@ import com.github.dockerjava.api.model.Ports.Binding; +import java.io.Serializable; + /** * 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 @@ -15,7 +17,9 @@ * 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 { +public class PortBinding implements Serializable { + private static final long serialVersionUID = 1L; + private final Binding binding; private final ExposedPort exposedPort; diff --git a/src/main/java/com/github/dockerjava/api/model/Ports.java b/src/main/java/com/github/dockerjava/api/model/Ports.java index 904a31c4e..673852d9e 100644 --- a/src/main/java/com/github/dockerjava/api/model/Ports.java +++ b/src/main/java/com/github/dockerjava/api/model/Ports.java @@ -16,6 +16,7 @@ import org.apache.commons.lang.builder.EqualsBuilder; import java.io.IOException; +import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -35,7 +36,8 @@ @SuppressWarnings(value = "checkstyle:equalshashcode") @JsonDeserialize(using = Ports.Deserializer.class) @JsonSerialize(using = Ports.Serializer.class) -public class Ports { +public class Ports implements Serializable { + private static final long serialVersionUID = 1L; private final Map ports = new HashMap(); diff --git a/src/main/java/com/github/dockerjava/api/model/PropagationMode.java b/src/main/java/com/github/dockerjava/api/model/PropagationMode.java new file mode 100644 index 000000000..9be7d6e43 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/PropagationMode.java @@ -0,0 +1,50 @@ +package com.github.dockerjava.api.model; + +/** + * The propagation mode of a file system or file: shared, slave or private. + * + * @see https://github.com/docker/docker/pull/17034 + * @since 1.22 + */ +public enum PropagationMode { + /** default */ + DEFAULT(""), + + /** shared */ + SHARED("shared"), + + /** slave */ + SLAVE("slave"), + + /** private */ + PRIVATE("private"); + + /** + * The default {@link PropagationMode}: {@link #DEFAULT} + */ + public static final PropagationMode DEFAULT_MODE = DEFAULT; + + private String value; + + PropagationMode(String v) { + value = v; + } + + @Override + public String toString() { + return value; + } + + public static PropagationMode fromString(String v) { + switch (v) { + case "shared": + return SHARED; + case "slave": + return SLAVE; + case "private": + return PRIVATE; + default: + return DEFAULT; + } + } +} diff --git a/src/main/java/com/github/dockerjava/api/model/Repository.java b/src/main/java/com/github/dockerjava/api/model/Repository.java index 1e814a1b5..f4e4b9ab8 100644 --- a/src/main/java/com/github/dockerjava/api/model/Repository.java +++ b/src/main/java/com/github/dockerjava/api/model/Repository.java @@ -1,5 +1,6 @@ package com.github.dockerjava.api.model; +import java.io.Serializable; import java.net.MalformedURLException; import java.net.URL; @@ -8,7 +9,9 @@ /** * A repository or image name. */ -public class Repository { +public class Repository implements Serializable { + private static final long serialVersionUID = 1L; + public final String name; /** diff --git a/src/main/java/com/github/dockerjava/api/model/ResponseItem.java b/src/main/java/com/github/dockerjava/api/model/ResponseItem.java index 12a090db8..6663b4bf0 100644 --- a/src/main/java/com/github/dockerjava/api/model/ResponseItem.java +++ b/src/main/java/com/github/dockerjava/api/model/ResponseItem.java @@ -15,7 +15,6 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) public class ResponseItem implements Serializable { - private static final long serialVersionUID = -5187169652557467828L; @JsonProperty("stream") diff --git a/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java b/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java index ac6c4a318..1f77b8b9d 100644 --- a/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java +++ b/src/main/java/com/github/dockerjava/api/model/RestartPolicy.java @@ -7,6 +7,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + /** * Container restart policy * @@ -26,7 +28,8 @@ * @author Marcus Linke * */ -public class RestartPolicy { +public class RestartPolicy implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("MaximumRetryCount") private Integer maximumRetryCount = 0; diff --git a/src/main/java/com/github/dockerjava/api/model/SearchItem.java b/src/main/java/com/github/dockerjava/api/model/SearchItem.java index 0a5843838..4e1663655 100644 --- a/src/main/java/com/github/dockerjava/api/model/SearchItem.java +++ b/src/main/java/com/github/dockerjava/api/model/SearchItem.java @@ -5,13 +5,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + /** * * @author Konstantin Pelykh (kpelykh@gmail.com) * */ @JsonIgnoreProperties(ignoreUnknown = true) -public class SearchItem { +public class SearchItem implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("star_count") private Integer starCount; diff --git a/src/main/java/com/github/dockerjava/api/model/Statistics.java b/src/main/java/com/github/dockerjava/api/model/Statistics.java index 21b9ca923..cf8c1b77e 100644 --- a/src/main/java/com/github/dockerjava/api/model/Statistics.java +++ b/src/main/java/com/github/dockerjava/api/model/Statistics.java @@ -1,5 +1,6 @@ package com.github.dockerjava.api.model; +import java.io.Serializable; import java.util.Map; import javax.annotation.CheckForNull; @@ -16,7 +17,8 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(Include.NON_NULL) -public class Statistics { +public class Statistics implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("read") private String read; diff --git a/src/main/java/com/github/dockerjava/api/model/Ulimit.java b/src/main/java/com/github/dockerjava/api/model/Ulimit.java index 7427fb0cc..abcf298bd 100644 --- a/src/main/java/com/github/dockerjava/api/model/Ulimit.java +++ b/src/main/java/com/github/dockerjava/api/model/Ulimit.java @@ -7,10 +7,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + /** * @author Vangie Du (duwan@live.com) */ -public class Ulimit { +public class Ulimit implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("Name") private String name; diff --git a/src/main/java/com/github/dockerjava/api/model/Version.java b/src/main/java/com/github/dockerjava/api/model/Version.java index 004c85f22..f08c411e2 100644 --- a/src/main/java/com/github/dockerjava/api/model/Version.java +++ b/src/main/java/com/github/dockerjava/api/model/Version.java @@ -8,6 +8,7 @@ import org.apache.commons.lang.builder.ToStringBuilder; import javax.annotation.CheckForNull; +import java.io.Serializable; /** * Used for `/version` @@ -16,7 +17,8 @@ * @see VersionCmd */ @JsonIgnoreProperties(ignoreUnknown = true) -public class Version { +public class Version implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("ApiVersion") private String apiVersion; diff --git a/src/main/java/com/github/dockerjava/api/model/Volume.java b/src/main/java/com/github/dockerjava/api/model/Volume.java index 655a8dbfc..46989f79c 100644 --- a/src/main/java/com/github/dockerjava/api/model/Volume.java +++ b/src/main/java/com/github/dockerjava/api/model/Volume.java @@ -3,12 +3,15 @@ import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; +import java.io.Serializable; + /** * Represents a bind mounted volume in a Docker container. * * @see Bind */ -public class Volume { +public class Volume implements Serializable { + private static final long serialVersionUID = 1L; private String path; diff --git a/src/main/java/com/github/dockerjava/api/model/VolumeBind.java b/src/main/java/com/github/dockerjava/api/model/VolumeBind.java index cf9c077ab..f78fc587e 100644 --- a/src/main/java/com/github/dockerjava/api/model/VolumeBind.java +++ b/src/main/java/com/github/dockerjava/api/model/VolumeBind.java @@ -1,6 +1,10 @@ package com.github.dockerjava.api.model; -public class VolumeBind { +import java.io.Serializable; + +public class VolumeBind implements Serializable { + private static final long serialVersionUID = 1L; + private final String hostPath; private final String 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 index d8303ec84..aeef19e9f 100644 --- a/src/main/java/com/github/dockerjava/api/model/VolumeBinds.java +++ b/src/main/java/com/github/dockerjava/api/model/VolumeBinds.java @@ -1,6 +1,7 @@ package com.github.dockerjava.api.model; import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -22,7 +23,9 @@ // This is not going to be serialized @JsonDeserialize(using = VolumeBinds.Deserializer.class) @JsonSerialize(using = VolumeBinds.Serializer.class) -public class VolumeBinds { +public class VolumeBinds implements Serializable { + private static final long serialVersionUID = 1L; + private final VolumeBind[] binds; public VolumeBinds(VolumeBind... binds) { diff --git a/src/main/java/com/github/dockerjava/api/model/VolumeRW.java b/src/main/java/com/github/dockerjava/api/model/VolumeRW.java index 7c4bdfb26..229cee3ed 100644 --- a/src/main/java/com/github/dockerjava/api/model/VolumeRW.java +++ b/src/main/java/com/github/dockerjava/api/model/VolumeRW.java @@ -1,6 +1,7 @@ package com.github.dockerjava.api.model; import java.io.IOException; +import java.io.Serializable; import java.util.Map.Entry; import org.apache.commons.lang.builder.EqualsBuilder; @@ -29,7 +30,8 @@ @JsonDeserialize(using = VolumeRW.Deserializer.class) @JsonSerialize(using = VolumeRW.Serializer.class) @Deprecated -public class VolumeRW { +public class VolumeRW implements Serializable { + private static final long serialVersionUID = 1L; private Volume volume; diff --git a/src/main/java/com/github/dockerjava/api/model/Volumes.java b/src/main/java/com/github/dockerjava/api/model/Volumes.java index 46175548d..3246b0e8e 100644 --- a/src/main/java/com/github/dockerjava/api/model/Volumes.java +++ b/src/main/java/com/github/dockerjava/api/model/Volumes.java @@ -1,6 +1,7 @@ package com.github.dockerjava.api.model; import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -21,7 +22,8 @@ @JsonSerialize(using = Volumes.Serializer.class) @JsonDeserialize(using = Volumes.Deserializer.class) -public class Volumes { +public class Volumes implements Serializable { + private static final long serialVersionUID = 1L; private Volume[] volumes; diff --git a/src/main/java/com/github/dockerjava/api/model/VolumesFrom.java b/src/main/java/com/github/dockerjava/api/model/VolumesFrom.java index f027b6d71..fb52ff3f7 100644 --- a/src/main/java/com/github/dockerjava/api/model/VolumesFrom.java +++ b/src/main/java/com/github/dockerjava/api/model/VolumesFrom.java @@ -1,6 +1,7 @@ package com.github.dockerjava.api.model; import java.io.IOException; +import java.io.Serializable; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; @@ -19,7 +20,8 @@ @JsonSerialize(using = VolumesFrom.Serializer.class) @JsonDeserialize(using = VolumesFrom.Deserializer.class) -public class VolumesFrom { +public class VolumesFrom implements Serializable { + private static final long serialVersionUID = 1L; private String container; diff --git a/src/main/java/com/github/dockerjava/api/model/VolumesRW.java b/src/main/java/com/github/dockerjava/api/model/VolumesRW.java index f54f8242c..1b12bd13d 100644 --- a/src/main/java/com/github/dockerjava/api/model/VolumesRW.java +++ b/src/main/java/com/github/dockerjava/api/model/VolumesRW.java @@ -1,6 +1,7 @@ package com.github.dockerjava.api.model; import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -22,7 +23,9 @@ // This is not going to be serialized @JsonSerialize(using = VolumesRW.Serializer.class) @JsonDeserialize(using = VolumesRW.Deserializer.class) -public class VolumesRW { +public class VolumesRW implements Serializable { + private static final long serialVersionUID = 1L; + private final VolumeRW[] volumesRW; public VolumesRW(VolumeRW... binds) { diff --git a/src/main/java/com/github/dockerjava/api/model/WaitResponse.java b/src/main/java/com/github/dockerjava/api/model/WaitResponse.java index a2ab16f6f..0cab338b2 100644 --- a/src/main/java/com/github/dockerjava/api/model/WaitResponse.java +++ b/src/main/java/com/github/dockerjava/api/model/WaitResponse.java @@ -3,11 +3,14 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; + /** * Represents a wait container command response */ @JsonIgnoreProperties(ignoreUnknown = true) -public class WaitResponse { +public class WaitResponse implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty("StatusCode") private Integer statusCode; diff --git a/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java b/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java new file mode 100644 index 000000000..a9a15bb36 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java @@ -0,0 +1,456 @@ +package com.github.dockerjava.core; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.apache.commons.lang.BooleanUtils.isTrue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.net.URI; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + +import com.github.dockerjava.api.exception.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; + +/** + * Respects some of the docker CLI options. See https://docs.docker.com/engine/reference/commandline/cli/#environment-variables + */ +public class DefaultDockerClientConfig implements Serializable, DockerClientConfig { + + private static final long serialVersionUID = 1L; + + public static final String DOCKER_HOST = "DOCKER_HOST"; + + public static final String DOCKER_TLS_VERIFY = "DOCKER_TLS_VERIFY"; + + public static final String DOCKER_CONFIG = "DOCKER_CONFIG"; + + public static final String DOCKER_CERT_PATH = "DOCKER_CERT_PATH"; + + public static final String API_VERSION = "api.version"; + + public static final String REGISTRY_USERNAME = "registry.username"; + + public static final String REGISTRY_PASSWORD = "registry.password"; + + public static final String REGISTRY_EMAIL = "registry.email"; + + public static final String REGISTRY_URL = "registry.url"; + + private static final String DOCKER_JAVA_PROPERTIES = "docker-java.properties"; + + private static final String DOCKER_CFG = ".dockercfg"; + + private static final Set CONFIG_KEYS = new HashSet(); + + static { + CONFIG_KEYS.add(DOCKER_HOST); + CONFIG_KEYS.add(DOCKER_TLS_VERIFY); + CONFIG_KEYS.add(DOCKER_CONFIG); + CONFIG_KEYS.add(DOCKER_CERT_PATH); + CONFIG_KEYS.add(API_VERSION); + CONFIG_KEYS.add(REGISTRY_USERNAME); + CONFIG_KEYS.add(REGISTRY_PASSWORD); + CONFIG_KEYS.add(REGISTRY_EMAIL); + CONFIG_KEYS.add(REGISTRY_URL); + } + + private final URI dockerHost; + + private final String registryUsername, registryPassword, registryEmail, registryUrl, dockerConfig; + + private final SSLConfig sslConfig; + + private final RemoteApiVersion apiVersion; + + DefaultDockerClientConfig(URI dockerHost, String dockerConfig, String apiVersion, String registryUrl, + String registryUsername, String registryPassword, String registryEmail, SSLConfig sslConfig) { + this.dockerHost = checkDockerHostScheme(dockerHost); + this.dockerConfig = dockerConfig; + this.apiVersion = RemoteApiVersion.parseConfigWithDefault(apiVersion); + this.sslConfig = sslConfig; + this.registryUsername = registryUsername; + this.registryPassword = registryPassword; + this.registryEmail = registryEmail; + this.registryUrl = registryUrl; + } + + private URI checkDockerHostScheme(URI dockerHost) { + if ("tcp".equals(dockerHost.getScheme()) || "unix".equals(dockerHost.getScheme())) { + return dockerHost; + } else { + throw new DockerClientException("Unsupported protocol scheme found: '" + dockerHost + + "'. Only 'tcp://' or 'unix://' supported."); + } + } + + private static Properties loadIncludedDockerProperties(Properties systemProperties) { + try (InputStream is = DefaultDockerClientConfig.class.getResourceAsStream("/" + DOCKER_JAVA_PROPERTIES)) { + Properties p = new Properties(); + p.load(is); + 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_JAVA_PROPERTIES); + if (usersDockerPropertiesFile.isFile()) { + try (FileInputStream in = new FileInputStream(usersDockerPropertiesFile)) { + overriddenProperties.load(in); + } 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)) { + overriddenProperties.setProperty(DOCKER_HOST, env.get(DOCKER_HOST)); + } + + for (Map.Entry envEntry : env.entrySet()) { + String envKey = envEntry.getKey(); + if (CONFIG_KEYS.contains(envKey)) { + overriddenProperties.setProperty(envKey, envEntry.getValue()); + } + } + + return overriddenProperties; + } + + /** + * 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 : CONFIG_KEYS) { + if (systemProperties.containsKey(key)) { + overriddenProperties.setProperty(key, systemProperties.getProperty(key)); + } + } + return overriddenProperties; + } + + public static Builder createDefaultConfigBuilder() { + return createDefaultConfigBuilder(System.getenv(), System.getProperties()); + } + + /** + * Allows you to build the config without system environment interfering for more robust testing + */ + static Builder createDefaultConfigBuilder(Map env, Properties systemProperties) { + Properties properties = loadIncludedDockerProperties(systemProperties); + properties = overrideDockerPropertiesWithSettingsFromUserHome(properties, systemProperties); + properties = overrideDockerPropertiesWithEnv(properties, env); + properties = overrideDockerPropertiesWithSystemProperties(properties, systemProperties); + return new Builder().withProperties(properties); + } + + @Override + public URI getDockerHost() { + return dockerHost; + } + + @Override + public RemoteApiVersion getApiVersion() { + return apiVersion; + } + + @Override + public String getRegistryUsername() { + return registryUsername; + } + + @Override + public String getRegistryPassword() { + return registryPassword; + } + + @Override + public String getRegistryEmail() { + return registryEmail; + } + + @Override + public String getRegistryUrl() { + return registryUrl; + } + + public String getDockerConfig() { + return dockerConfig; + } + + private AuthConfig getAuthConfig() { + AuthConfig authConfig = null; + if (getRegistryUsername() != null && getRegistryPassword() != null && getRegistryEmail() != null + && getRegistryUrl() != null) { + authConfig = new AuthConfig() + .withUsername(getRegistryUsername()) + .withPassword(getRegistryPassword()) + .withEmail(getRegistryEmail()) + .withRegistryAddress(getRegistryUrl()); + } + return authConfig; + } + + @Override + public AuthConfig effectiveAuthConfig(String imageName) { + AuthConfig authConfig = null; + + File dockerCfgFile = new File(getDockerConfig() + File.separator + DOCKER_CFG); + + if (dockerCfgFile.exists() && dockerCfgFile.isFile() && imageName != null) { + AuthConfigFile authConfigFile; + try { + authConfigFile = AuthConfigFile.loadConfig(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 otherAuthConfig = getAuthConfig(); + + if (otherAuthConfig != null) { + authConfig = otherAuthConfig; + } + + return authConfig; + } + + @Override + public AuthConfigurations getAuthConfigurations() { + File dockerCfgFile = new File(getDockerConfig() + File.separator + DOCKER_CFG); + if (dockerCfgFile.exists() && dockerCfgFile.isFile()) { + AuthConfigFile authConfigFile; + try { + authConfigFile = AuthConfigFile.loadConfig(dockerCfgFile); + } catch (IOException e) { + throw new DockerClientException("Failed to parse dockerCfgFile", e); + } + + return authConfigFile.getAuthConfigurations(); + } + + return new AuthConfigurations(); + } + + @Override + public SSLConfig getSSLConfig() { + return sslConfig; + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } + + public static class Builder { + private URI dockerHost; + + private String apiVersion, registryUsername, registryPassword, registryEmail, registryUrl, dockerConfig, + dockerCertPath; + + private Boolean dockerTlsVerify; + + private SSLConfig customSslConfig = null; + + /** + * This will set all fields in the builder to those contained in the Properties object. The Properties object should contain the + * following docker-java configuration keys: DOCKER_HOST, DOCKER_TLS_VERIFY, api.version, registry.username, registry.password, + * registry.email, DOCKER_CERT_PATH, and DOCKER_CONFIG. + */ + public Builder withProperties(Properties p) { + return withDockerHost(p.getProperty(DOCKER_HOST)) + .withDockerTlsVerify(p.getProperty(DOCKER_TLS_VERIFY)) + .withDockerConfig(p.getProperty(DOCKER_CONFIG)) + .withDockerCertPath(p.getProperty(DOCKER_CERT_PATH)) + .withApiVersion(p.getProperty(API_VERSION)) + .withRegistryUsername(p.getProperty(REGISTRY_USERNAME)) + .withRegistryPassword(p.getProperty(REGISTRY_PASSWORD)) + .withRegistryEmail(p.getProperty(REGISTRY_EMAIL)) + .withRegistryUrl(p.getProperty(REGISTRY_URL)); + } + + /** + * configure DOCKER_HOST + */ + public final Builder withDockerHost(String dockerHost) { + checkNotNull(dockerHost, "uri was not specified"); + this.dockerHost = URI.create(dockerHost); + return this; + } + + public final Builder withApiVersion(RemoteApiVersion apiVersion) { + this.apiVersion = apiVersion.getVersion(); + return this; + } + + public final Builder withApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + return this; + } + + public final Builder withRegistryUsername(String registryUsername) { + this.registryUsername = registryUsername; + return this; + } + + public final Builder withRegistryPassword(String registryPassword) { + this.registryPassword = registryPassword; + return this; + } + + public final Builder withRegistryEmail(String registryEmail) { + this.registryEmail = registryEmail; + return this; + } + + public Builder withRegistryUrl(String registryUrl) { + this.registryUrl = registryUrl; + return this; + } + + public final Builder withDockerCertPath(String dockerCertPath) { + this.dockerCertPath = dockerCertPath; + return this; + } + + public final Builder withDockerConfig(String dockerConfig) { + this.dockerConfig = dockerConfig; + return this; + } + + public final Builder withDockerTlsVerify(String dockerTlsVerify) { + if (dockerTlsVerify != null) { + String trimmed = dockerTlsVerify.trim(); + this.dockerTlsVerify = "true".equalsIgnoreCase(trimmed) || "1".equals(trimmed); + } else { + this.dockerTlsVerify = false; + } + return this; + } + + public final Builder withDockerTlsVerify(Boolean dockerTlsVerify) { + this.dockerTlsVerify = dockerTlsVerify; + return this; + } + + /** + * Overrides the default {@link SSLConfig} that is used when calling {@link Builder#withDockerTlsVerify(java.lang.Boolean)} and + * {@link Builder#withDockerCertPath(String)}. This way it is possible to pass a custom {@link SSLConfig} to the resulting + * {@link DockerClientConfig} that may be created by other means than the local file system. + */ + public final Builder withCustomSslConfig(SSLConfig customSslConfig) { + this.customSslConfig = customSslConfig; + return this; + } + + public DefaultDockerClientConfig build() { + + SSLConfig sslConfig = null; + + if (customSslConfig == null) { + if (isTrue(dockerTlsVerify)) { + dockerCertPath = checkDockerCertPath(dockerCertPath); + sslConfig = new LocalDirectorySSLConfig(dockerCertPath); + } + } else { + sslConfig = customSslConfig; + } + + return new DefaultDockerClientConfig(dockerHost, dockerConfig, apiVersion, registryUrl, registryUsername, + registryPassword, registryEmail, sslConfig); + } + + private String checkDockerCertPath(String dockerCertPath) { + if (StringUtils.isEmpty(dockerCertPath)) { + throw new DockerClientException( + "Enabled TLS verification (DOCKER_TLS_VERIFY=1) but certifate path (DOCKER_CERT_PATH) is not defined."); + } + + File certPath = new File(dockerCertPath); + + if (!certPath.exists()) { + throw new DockerClientException( + "Enabled TLS verification (DOCKER_TLS_VERIFY=1) but certificate path (DOCKER_CERT_PATH) '" + + dockerCertPath + "' doesn't exist."); + } else if (!certPath.isDirectory()) { + throw new DockerClientException( + "Enabled TLS verification (DOCKER_TLS_VERIFY=1) but certificate path (DOCKER_CERT_PATH) '" + + dockerCertPath + "' doesn't point to a directory."); + } + + return dockerCertPath; + } + } +} diff --git a/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java b/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java index 6aed6c358..991bdbb86 100644 --- a/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java +++ b/src/main/java/com/github/dockerjava/core/DockerClientBuilder.java @@ -2,8 +2,8 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.DockerCmdExecFactory; -import com.github.dockerjava.core.DockerClientConfig.DockerClientConfigBuilder; -import com.github.dockerjava.jaxrs.DockerCmdExecFactoryImpl; +import com.github.dockerjava.core.DefaultDockerClientConfig.Builder; +import com.github.dockerjava.jaxrs.JerseyDockerCmdExecFactory; public class DockerClientBuilder { @@ -19,7 +19,7 @@ public static DockerClientBuilder getInstance() { return new DockerClientBuilder(DockerClientImpl.getInstance()); } - public static DockerClientBuilder getInstance(DockerClientConfigBuilder dockerClientConfigBuilder) { + public static DockerClientBuilder getInstance(Builder dockerClientConfigBuilder) { return getInstance(dockerClientConfigBuilder.build()); } @@ -32,7 +32,7 @@ public static DockerClientBuilder getInstance(String serverUrl) { } public static DockerCmdExecFactory getDefaultDockerCmdExecFactory() { - return new DockerCmdExecFactoryImpl(); + return new JerseyDockerCmdExecFactory(); } public DockerClientBuilder withDockerCmdExecFactory(DockerCmdExecFactory dockerCmdExecFactory) { diff --git a/src/main/java/com/github/dockerjava/core/DockerClientConfig.java b/src/main/java/com/github/dockerjava/core/DockerClientConfig.java index 31cfc0c58..532fe6b43 100644 --- a/src/main/java/com/github/dockerjava/core/DockerClientConfig.java +++ b/src/main/java/com/github/dockerjava/core/DockerClientConfig.java @@ -1,440 +1,40 @@ +/* + * Created on 08.06.2016 + */ 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.InputStream; -import java.io.Serializable; import java.net.URI; -import java.util.HashSet; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.builder.EqualsBuilder; -import org.apache.commons.lang.builder.HashCodeBuilder; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; -import com.github.dockerjava.api.exception.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; /** - * Respects some of the docker CLI options. See https://docs.docker.com/engine/reference/commandline/cli/#environment-variables + * Interface that describes the docker client configuration. + * + * @author Marcus Linke + * */ -public class DockerClientConfig implements Serializable { - - private static final long serialVersionUID = -4307357472441531489L; - - public static final String DOCKER_HOST = "DOCKER_HOST"; - - public static final String DOCKER_TLS_VERIFY = "DOCKER_TLS_VERIFY"; - - public static final String DOCKER_CONFIG = "DOCKER_CONFIG"; - - public static final String DOCKER_CERT_PATH = "DOCKER_CERT_PATH"; - - public static final String API_VERSION = "api.version"; - - public static final String REGISTRY_USERNAME = "registry.username"; - - public static final String REGISTRY_PASSWORD = "registry.password"; - - public static final String REGISTRY_EMAIL = "registry.email"; - - public static final String REGISTRY_URL = "registry.url"; - - private static final String DOCKER_JAVA_PROPERTIES = "docker-java.properties"; - - private static final String DOCKER_CFG = ".dockercfg"; - - private static final Set CONFIG_KEYS = new HashSet(); - - static { - CONFIG_KEYS.add(DOCKER_HOST); - CONFIG_KEYS.add(DOCKER_TLS_VERIFY); - CONFIG_KEYS.add(DOCKER_CONFIG); - CONFIG_KEYS.add(DOCKER_CERT_PATH); - CONFIG_KEYS.add(API_VERSION); - CONFIG_KEYS.add(REGISTRY_USERNAME); - CONFIG_KEYS.add(REGISTRY_PASSWORD); - CONFIG_KEYS.add(REGISTRY_EMAIL); - CONFIG_KEYS.add(REGISTRY_URL); - } - - private final URI dockerHost; - - private final String registryUsername, registryPassword, registryEmail, registryUrl, dockerConfig, dockerCertPath; +public interface DockerClientConfig { - private final boolean dockerTlsVerify; + URI getDockerHost(); - private final RemoteApiVersion apiVersion; + RemoteApiVersion getApiVersion(); - DockerClientConfig(URI dockerHost, String dockerConfig, String apiVersion, String registryUrl, - String registryUsername, String registryPassword, String registryEmail, String dockerCertPath, - boolean dockerTslVerify) { - this.dockerHost = checkDockerHostScheme(dockerHost); - this.dockerTlsVerify = dockerTslVerify; - this.dockerCertPath = checkDockerCertPath(dockerTslVerify, dockerCertPath); - this.dockerConfig = dockerConfig; - this.apiVersion = RemoteApiVersion.parseConfigWithDefault(apiVersion); - this.registryUsername = registryUsername; - this.registryPassword = registryPassword; - this.registryEmail = registryEmail; - this.registryUrl = registryUrl; - } + String getRegistryUsername(); - private URI checkDockerHostScheme(URI dockerHost) { - if ("tcp".equals(dockerHost.getScheme()) || "unix".equals(dockerHost.getScheme())) { - return dockerHost; - } else { - throw new DockerClientException("Unsupported protocol scheme found: '" + dockerHost - + "'. Only 'tcp://' or 'unix://' supported."); - } - } + String getRegistryPassword(); - private String checkDockerCertPath(boolean dockerTlsVerify, String dockerCertPath) { - if (dockerTlsVerify) { - if (StringUtils.isEmpty(dockerCertPath)) { - throw new DockerClientException( - "Enabled TLS verification (DOCKER_TLS_VERIFY=1) but certifate path (DOCKER_CERT_PATH) is not defined."); - } else { - File certPath = new File(dockerCertPath); + String getRegistryEmail(); - if (!certPath.exists()) { - throw new DockerClientException( - "Enabled TLS verification (DOCKER_TLS_VERIFY=1) but certificate path (DOCKER_CERT_PATH) '" + dockerCertPath - + "' doesn't exist."); - } + String getRegistryUrl(); - if (certPath.isDirectory()) { - return dockerCertPath; - } else { - throw new DockerClientException( - "Enabled TLS verification (DOCKER_TLS_VERIFY=1) but certificate path (DOCKER_CERT_PATH) '" + dockerCertPath - + "' doesn't point to a directory."); - } - } - } else { - return dockerCertPath; - } - } + AuthConfig effectiveAuthConfig(String imageName); - private static Properties loadIncludedDockerProperties(Properties systemProperties) { - try (InputStream is = DockerClientConfig.class.getResourceAsStream("/" + DOCKER_JAVA_PROPERTIES)) { - Properties p = new Properties(); - p.load(is); - 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_JAVA_PROPERTIES); - if (usersDockerPropertiesFile.isFile()) { - try (FileInputStream in = new FileInputStream(usersDockerPropertiesFile)) { - overriddenProperties.load(in); - } 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)) { - overriddenProperties.setProperty(DOCKER_HOST, env.get(DOCKER_HOST)); - } - - for (Map.Entry envEntry : env.entrySet()) { - String envKey = envEntry.getKey(); - if (CONFIG_KEYS.contains(envKey)) { - overriddenProperties.setProperty(envKey, envEntry.getValue()); - } - } - - return overriddenProperties; - } + AuthConfigurations getAuthConfigurations(); /** - * 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 + * Returns an {@link SSLConfig} when secure connection is configured or null if not. */ - private static Properties overrideDockerPropertiesWithSystemProperties(Properties p, Properties systemProperties) { - Properties overriddenProperties = new Properties(); - overriddenProperties.putAll(p); - - for (String key : CONFIG_KEYS) { - 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 getDockerHost() { - return dockerHost; - } - - public RemoteApiVersion getApiVersion() { - return apiVersion; - } - - public String getRegistryUsername() { - return registryUsername; - } - - public String getRegistryPassword() { - return registryPassword; - } - - public String getRegistryEmail() { - return registryEmail; - } - - public String getRegistryUrl() { - return registryUrl; - } - - public String getDockerConfig() { - return dockerConfig; - } - - public String getDockerCertPath() { - return dockerCertPath; - } - - public boolean getDockerTlsVerify() { - return dockerTlsVerify; - } - - private AuthConfig getAuthConfig() { - AuthConfig authConfig = null; - if (getRegistryUsername() != null && getRegistryPassword() != null && getRegistryEmail() != null - && getRegistryUrl() != null) { - authConfig = new AuthConfig() - .withUsername(getRegistryUsername()) - .withPassword(getRegistryPassword()) - .withEmail(getRegistryEmail()) - .withRegistryAddress(getRegistryUrl()); - } - return authConfig; - } - - public AuthConfig effectiveAuthConfig(String imageName) { - AuthConfig authConfig = null; - - File dockerCfgFile = new File(getDockerConfig() + File.separator + DOCKER_CFG); - - if (dockerCfgFile.exists() && dockerCfgFile.isFile() && imageName != null) { - AuthConfigFile authConfigFile; - try { - authConfigFile = AuthConfigFile.loadConfig(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 otherAuthConfig = getAuthConfig(); - - if (otherAuthConfig != null) { - authConfig = otherAuthConfig; - } - - return authConfig; - } - - public AuthConfigurations getAuthConfigurations() { - File dockerCfgFile = new File(getDockerConfig() + File.separator + DOCKER_CFG); - if (dockerCfgFile.exists() && dockerCfgFile.isFile()) { - AuthConfigFile authConfigFile; - try { - authConfigFile = AuthConfigFile.loadConfig(dockerCfgFile); - } catch (IOException e) { - throw new DockerClientException("Failed to parse dockerCfgFile", e); - } - - return authConfigFile.getAuthConfigurations(); - } - - return new AuthConfigurations(); - } - - // CHECKSTYLE:OFF - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - DockerClientConfig that = (DockerClientConfig) o; - - return EqualsBuilder.reflectionEquals(this, that); - } - - @Override - public int hashCode() { - return HashCodeBuilder.reflectionHashCode(this); - } - - // CHECKSTYLE:ON - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } - - public static class DockerClientConfigBuilder { - private URI dockerHost; - - private String apiVersion, registryUsername, registryPassword, registryEmail, registryUrl, dockerConfig, - dockerCertPath; - - private boolean dockerTlsVerify; - - /** - * This will set all fields in the builder to those contained in the Properties object. The Properties object should contain the - * following docker-java configuration keys: DOCKER_HOST, DOCKER_TLS_VERIFY, api.version, registry.username, registry.password, - * registry.email, DOCKER_CERT_PATH, and DOCKER_CONFIG. - */ - public DockerClientConfigBuilder withProperties(Properties p) { - return withDockerHost(p.getProperty(DOCKER_HOST)).withDockerTlsVerify(p.getProperty(DOCKER_TLS_VERIFY)) - .withDockerConfig(p.getProperty(DOCKER_CONFIG)).withDockerCertPath(p.getProperty(DOCKER_CERT_PATH)) - .withApiVersion(p.getProperty(API_VERSION)).withRegistryUsername(p.getProperty(REGISTRY_USERNAME)) - .withRegistryPassword(p.getProperty(REGISTRY_PASSWORD)) - .withRegistryEmail(p.getProperty(REGISTRY_EMAIL)).withRegistryUrl(p.getProperty(REGISTRY_URL)); - } - - /** - * configure DOCKER_HOST - */ - public final DockerClientConfigBuilder withDockerHost(String dockerHost) { - checkNotNull(dockerHost, "uri was not specified"); - this.dockerHost = URI.create(dockerHost); - return this; - } - - public final DockerClientConfigBuilder withApiVersion(RemoteApiVersion apiVersion) { - this.apiVersion = apiVersion.getVersion(); - return this; - } - - public final DockerClientConfigBuilder withApiVersion(String apiVersion) { - this.apiVersion = apiVersion; - return this; - } - - public final DockerClientConfigBuilder withRegistryUsername(String registryUsername) { - this.registryUsername = registryUsername; - return this; - } - - public final DockerClientConfigBuilder withRegistryPassword(String registryPassword) { - this.registryPassword = registryPassword; - return this; - } - - public final DockerClientConfigBuilder withRegistryEmail(String registryEmail) { - this.registryEmail = registryEmail; - return this; - } - - public DockerClientConfigBuilder withRegistryUrl(String registryUrl) { - this.registryUrl = registryUrl; - return this; - } - - public final DockerClientConfigBuilder withDockerCertPath(String dockerCertPath) { - this.dockerCertPath = dockerCertPath; - return this; - } - - public final DockerClientConfigBuilder withDockerConfig(String dockerConfig) { - this.dockerConfig = dockerConfig; - return this; - } - - public final DockerClientConfigBuilder withDockerTlsVerify(String dockerTlsVerify) { - if (dockerTlsVerify != null) { - String trimmed = dockerTlsVerify.trim(); - this.dockerTlsVerify = "true".equalsIgnoreCase(trimmed) || "1".equals(trimmed); - } else { - this.dockerTlsVerify = false; - } - return this; - } - - public final DockerClientConfigBuilder withDockerTlsVerify(Boolean dockerTlsVerify) { - this.dockerTlsVerify = dockerTlsVerify; - return this; - } + SSLConfig getSSLConfig(); - public DockerClientConfig build() { - return new DockerClientConfig(dockerHost, dockerConfig, apiVersion, registryUrl, registryUsername, - registryPassword, registryEmail, dockerCertPath, dockerTlsVerify); - } - } } diff --git a/src/main/java/com/github/dockerjava/core/DockerClientImpl.java b/src/main/java/com/github/dockerjava/core/DockerClientImpl.java index 765f562bd..2d0b5caab 100644 --- a/src/main/java/com/github/dockerjava/core/DockerClientImpl.java +++ b/src/main/java/com/github/dockerjava/core/DockerClientImpl.java @@ -37,6 +37,7 @@ import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.command.ListNetworksCmd; import com.github.dockerjava.api.command.ListVolumesCmd; +import com.github.dockerjava.api.command.LoadImageCmd; import com.github.dockerjava.api.command.LogContainerCmd; import com.github.dockerjava.api.command.PauseContainerCmd; import com.github.dockerjava.api.command.PingCmd; @@ -89,6 +90,7 @@ import com.github.dockerjava.core.command.ListImagesCmdImpl; import com.github.dockerjava.core.command.ListNetworksCmdImpl; import com.github.dockerjava.core.command.ListVolumesCmdImpl; +import com.github.dockerjava.core.command.LoadImageCmdImpl; import com.github.dockerjava.core.command.LogContainerCmdImpl; import com.github.dockerjava.core.command.PauseContainerCmdImpl; import com.github.dockerjava.core.command.PingCmdImpl; @@ -125,7 +127,7 @@ public class DockerClientImpl implements Closeable, DockerClient { private DockerCmdExecFactory dockerCmdExecFactory; private DockerClientImpl() { - this(DockerClientConfig.createDefaultConfigBuilder().build()); + this(DefaultDockerClientConfig.createDefaultConfigBuilder().build()); } private DockerClientImpl(String serverUrl) { @@ -138,7 +140,7 @@ private DockerClientImpl(DockerClientConfig dockerClientConfig) { } private static DockerClientConfig configWithServerUrl(String serverUrl) { - return DockerClientConfig.createDefaultConfigBuilder().withDockerHost(serverUrl).build(); + return DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost(serverUrl).build(); } public static DockerClientImpl getInstance() { @@ -249,6 +251,11 @@ public CreateImageCmd createImageCmd(String repository, InputStream imageStream) return new CreateImageCmdImpl(getDockerCmdExecFactory().createCreateImageCmdExec(), repository, imageStream); } + @Override + public LoadImageCmd loadImageCmd(@Nonnull InputStream imageStream) { + return new LoadImageCmdImpl(getDockerCmdExecFactory().createLoadImageCmdExec(), imageStream); + } + @Override public SearchImagesCmd searchImagesCmd(String term) { return new SearchImagesCmdImpl(getDockerCmdExecFactory().createSearchImagesCmdExec(), term); diff --git a/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java b/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java index fa5dba722..b26b7db44 100644 --- a/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java +++ b/src/main/java/com/github/dockerjava/core/GoLangFileMatch.java @@ -6,6 +6,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; @@ -49,6 +50,8 @@ private GoLangFileMatch() { public static final boolean IS_WINDOWS = File.separatorChar == '\\'; + private static final String PATTERN_CHARS_TO_ESCAPE = "\\.[]{}()*+-?^$|"; + public static boolean match(List patterns, File file) { return !match(patterns, file.getPath()).isEmpty(); } @@ -71,196 +74,188 @@ public static List match(List patterns, String name) { } 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); + return buildPattern(pattern).matcher(name).matches(); + } - // 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; - } - } + private static Pattern buildPattern(String pattern) { + StringBuilder patternStringBuilder = new StringBuilder("^"); + while (!pattern.isEmpty()) { + pattern = appendChunkPattern(patternStringBuilder, pattern); + + if (!pattern.isEmpty()) { + patternStringBuilder.append(quote(File.separatorChar)); } - return false; } - return name.isEmpty(); + patternStringBuilder.append("(").append(quote(File.separatorChar)).append(".*").append(")?"); + return Pattern.compile(patternStringBuilder.toString()); } - static ScanResult scanChunk(String pattern) { - boolean star = false; - if (!pattern.isEmpty() && pattern.charAt(0) == '*') { - pattern = pattern.substring(1); - star = true; + private static String quote(char separatorChar) { + if (StringUtils.contains(PATTERN_CHARS_TO_ESCAPE, separatorChar)) { + return "\\" + separatorChar; + } else { + return String.valueOf(separatorChar); } + } + + private static String appendChunkPattern(StringBuilder patternStringBuilder, String pattern) { + if (pattern.equals("**") || pattern.startsWith("**" + File.separator)) { + patternStringBuilder.append("(") + .append("[^").append(quote(File.separatorChar)).append("]*") + .append("(") + .append(quote(File.separatorChar)).append("[^").append(quote(File.separatorChar)).append("]*") + .append(")*").append(")?"); + return pattern.substring(pattern.length() == 2 ? 2 : 3); + } + boolean inRange = false; + int rangeFrom = 0; + RangeParseState rangeParseState = RangeParseState.CHAR_EXPECTED; + boolean isEsc = false; int i; - Scan: for (i = 0; i < pattern.length(); i++) { - switch (pattern.charAt(i)) { - case '\\': { - if (!IS_WINDOWS && i + 1 < pattern.length()) { - i++; + for (i = 0; i < pattern.length(); i++) { + char c = pattern.charAt(i); + switch (c) { + case '/': + if (!inRange) { + if (!IS_WINDOWS && !isEsc) { + // end of chunk + return pattern.substring(i + 1); + } else { + patternStringBuilder.append(quote(c)); + } + } else { + rangeParseState = nextStateAfterChar(rangeParseState); } + isEsc = false; break; - } - case '[': - inRange = true; - break; - case ']': - inRange = false; - break; - case '*': + case '\\': if (!inRange) { - break Scan; + if (!IS_WINDOWS) { + if (isEsc) { + patternStringBuilder.append(quote(c)); + isEsc = false; + } else { + isEsc = true; + } + } else { + // end of chunk + return pattern.substring(i + 1); + } + } else { + if (IS_WINDOWS || isEsc) { + rangeParseState = nextStateAfterChar(rangeParseState); + isEsc = false; + } else { + isEsc = true; + } } - } - } - 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)) { + break; 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++; + if (!isEsc) { + if (inRange) { + throw new GoLangFileMatchException("[ not expected, closing bracket ] not yet reached"); + } + rangeFrom = i; + rangeParseState = RangeParseState.CHAR_EXPECTED; + inRange = true; + } else { + if (!inRange) { + patternStringBuilder.append(c); + } else { + rangeParseState = nextStateAfterChar(rangeParseState); + } } - // parse all ranges - boolean match = false; - int nrange = 0; - while (true) { - if (chunkOffset < chunkLength && chunk.charAt(chunkOffset) == ']' && nrange > 0) { - chunkOffset++; - break; + isEsc = false; + break; + case ']': + if (!isEsc) { + if (!inRange) { + throw new GoLangFileMatchException("] is not expected, [ was not met"); } - 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 (rangeParseState == RangeParseState.CHAR_EXPECTED_AFTER_DASH) { + throw new GoLangFileMatchException("Character range not finished"); } - if (lo <= r && r <= hi) { - match = true; + patternStringBuilder.append(pattern.substring(rangeFrom, i + 1)); + inRange = false; + } else { + if (!inRange) { + patternStringBuilder.append(c); + } else { + rangeParseState = nextStateAfterChar(rangeParseState); } - nrange++; } - if (match == negated) { - return null; + isEsc = false; + break; + case '*': + if (!inRange) { + if (!isEsc) { + patternStringBuilder.append("[^").append(quote(File.separatorChar)).append("]*"); + } else { + patternStringBuilder.append(quote(c)); + } + } else { + rangeParseState = nextStateAfterChar(rangeParseState); } + isEsc = false; break; - case '?': - if (s.charAt(sOffset) == File.separatorChar) { - return null; + if (!inRange) { + if (!isEsc) { + patternStringBuilder.append("[^").append(quote(File.separatorChar)).append("]"); + } else { + patternStringBuilder.append(quote(c)); + } + } else { + rangeParseState = nextStateAfterChar(rangeParseState); } - sOffset++; - chunkOffset++; + isEsc = false; break; - case '\\': - if (!IS_WINDOWS) { - chunkOffset++; - if (chunkOffset == chunkLength) { - throw new GoLangFileMatchException(); + case '-': + if (!inRange) { + patternStringBuilder.append(quote(c)); + } else { + if (!isEsc) { + if (rangeParseState != RangeParseState.CHAR_OR_DASH_EXPECTED) { + throw new GoLangFileMatchException("- character not expected"); + } + rangeParseState = RangeParseState.CHAR_EXPECTED_AFTER_DASH; + } else { + rangeParseState = nextStateAfterChar(rangeParseState); } } - // fallthrough + isEsc = false; + break; default: - if (chunk.charAt(chunkOffset) != s.charAt(sOffset)) { - return null; + if (!inRange) { + patternStringBuilder.append(quote(c)); + } else { + rangeParseState = nextStateAfterChar(rangeParseState); } - sOffset++; - chunkOffset++; + isEsc = false; } } - 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(); - } - + if (isEsc) { + throw new GoLangFileMatchException("Escaped character missing"); } - r = chunk.charAt(chunkOffset); - chunkOffset++; - if (chunkOffset == chunkLength) { - throw new GoLangFileMatchException(); + if (inRange) { + throw new GoLangFileMatchException("Character range not finished"); } - return new GetEscResult(r, chunkOffset); + return ""; } - private static final class ScanResult { - public boolean star; - - public String chunk; - - public String pattern; - - ScanResult(boolean star, String chunk, String pattern) { - this.star = star; - this.chunk = chunk; - this.pattern = pattern; + private static RangeParseState nextStateAfterChar(RangeParseState currentState) { + if (currentState == RangeParseState.CHAR_EXPECTED_AFTER_DASH) { + return RangeParseState.CHAR_EXPECTED; + } else { + return RangeParseState.CHAR_OR_DASH_EXPECTED; } } - private static final class GetEscResult { - public char lo; - - public int chunkOffset; - - GetEscResult(char lo, int chunkOffset) { - this.lo = lo; - this.chunkOffset = chunkOffset; - } + private enum RangeParseState { + CHAR_EXPECTED, + CHAR_OR_DASH_EXPECTED, + CHAR_EXPECTED_AFTER_DASH } } diff --git a/src/main/java/com/github/dockerjava/core/LocalDirectorySSLConfig.java b/src/main/java/com/github/dockerjava/core/LocalDirectorySSLConfig.java index e39a2bc40..961c282de 100644 --- a/src/main/java/com/github/dockerjava/core/LocalDirectorySSLConfig.java +++ b/src/main/java/com/github/dockerjava/core/LocalDirectorySSLConfig.java @@ -2,7 +2,10 @@ import static com.google.common.base.Preconditions.checkNotNull; +import java.io.File; import java.io.Serializable; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.Security; import javax.net.ssl.SSLContext; @@ -50,9 +53,17 @@ public SSLContext getSSLContext() { System.setProperty("https.protocols", httpProtocols); } - sslConfig.keyStore(CertificateUtils.createKeyStore(dockerCertPath)); + String caPemPath = dockerCertPath + File.separator + "ca.pem"; + String keyPemPath = dockerCertPath + File.separator + "key.pem"; + String certPemPath = dockerCertPath + File.separator + "cert.pem"; + + String keypem = new String(Files.readAllBytes(Paths.get(keyPemPath))); + String certpem = new String(Files.readAllBytes(Paths.get(certPemPath))); + String capem = new String(Files.readAllBytes(Paths.get(caPemPath))); + + sslConfig.keyStore(CertificateUtils.createKeyStore(keypem, certpem)); sslConfig.keyStorePassword("docker"); - sslConfig.trustStore(CertificateUtils.createTrustStore(dockerCertPath)); + sslConfig.trustStore(CertificateUtils.createTrustStore(capem)); return sslConfig.createSSLContext(); diff --git a/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java b/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java index 992125414..f40b5b4d5 100644 --- a/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java +++ b/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java @@ -22,6 +22,11 @@ public class RemoteApiVersion implements Serializable { private static final Pattern VERSION_REGEX = Pattern.compile("v?(\\d+)\\.(\\d+)"); + /** + * Online documentation is not available anymore. + */ + public static final RemoteApiVersion VERSION_1_7 = RemoteApiVersion.create(1, 7); + /** * @see Docker API 1.16 */ @@ -57,6 +62,16 @@ public class RemoteApiVersion implements Serializable { */ public static final RemoteApiVersion VERSION_1_22 = RemoteApiVersion.create(1, 22); + /** + * @see Docker API 1.22 + */ + public static final RemoteApiVersion VERSION_1_23 = RemoteApiVersion.create(1, 23); + + /** + * @see Docker API 1.22 + */ + public static final RemoteApiVersion VERSION_1_24 = RemoteApiVersion.create(1, 24); + /** * Unknown, docker doesn't reflect reality. I.e. we implemented method, but for javadoc it not clear when it was added. */ diff --git a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java index f120012af..2238dfcea 100644 --- a/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java +++ b/src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java @@ -75,11 +75,13 @@ public void onComplete() { @Override public void close() throws IOException { - closed = true; - if (stream != null) { - stream.close(); + if (!closed) { + closed = true; + if (stream != null) { + stream.close(); + } + completed.countDown(); } - completed.countDown(); } /** @@ -99,7 +101,9 @@ public RC_T awaitCompletion() throws InterruptedException { * before {@link ResultCallback#onComplete()} was called. */ public boolean awaitCompletion(long timeout, TimeUnit timeUnit) throws InterruptedException { - return completed.await(timeout, timeUnit); + boolean result = completed.await(timeout, timeUnit); + getFirstError(); + return result; } /** diff --git a/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java index 487f0b264..c34b05155 100644 --- a/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java @@ -56,6 +56,8 @@ public class BuildImageCmdImpl extends AbstrAsyncDockerCmd buildArgs; + private Map labels; + public BuildImageCmdImpl(BuildImageCmd.Exec exec) { super(exec); } @@ -149,6 +151,11 @@ public Map getBuildArgs() { return buildArgs; } + @Override + public Map getLabels() { + return labels; + } + // getter lib specific @Override @@ -303,6 +310,15 @@ public BuildImageCmd withShmsize(Long shmsize) { return this; } + /** + * @see #labels + */ + @Override + public BuildImageCmd withLabels(Map labels) { + this.labels = labels; + return this; + } + @Override public void close() { super.close(); diff --git a/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java index 561871a09..7c224eec1 100644 --- a/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java @@ -472,6 +472,11 @@ public String getPidMode() { return hostConfig.getPidMode(); } + @Override + public HostConfig getHostConfig() { + return hostConfig; + } + @Override public String getCgroupParent() { return hostConfig.getCgroupParent(); @@ -983,6 +988,11 @@ public CreateContainerCmd withPidMode(String pidMode) { } @Override + public CreateContainerCmd withHostConfig(HostConfig hostConfig) { + this.hostConfig = hostConfig; + return this; + } + public CreateContainerCmd withSecurityOpts(String... securityOpt) { this.hostConfig.withSecurityOpts(securityOpt); return this; diff --git a/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java index 14da4626a..fc8bf1c0b 100644 --- a/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/ExecCreateCmdImpl.java @@ -26,6 +26,12 @@ public class ExecCreateCmdImpl extends AbstrDockerCmd implements LoadImageCmd { + + private InputStream imageStream; + + /** + * @param imageStream + * the InputStream of the tar file + */ + public LoadImageCmdImpl(LoadImageCmd.Exec exec, InputStream imageStream) { + super(exec); + withImageStream(imageStream); + } + + @Override + public InputStream getImageStream() { + return imageStream; + } + + /** + * @param imageStream + * the InputStream of the tar file + */ + @Override + public LoadImageCmdImpl withImageStream(@Nonnull InputStream imageStream) { + checkNotNull(imageStream, "imageStream was not specified"); + this.imageStream = imageStream; + return this; + } +} diff --git a/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java b/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java index c970d6c2d..9dcd20199 100644 --- a/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java +++ b/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java @@ -204,9 +204,10 @@ private List matchingIgnorePatterns(String fileName) { int lineNumber = 0; for (String pattern : ignores) { + String goLangPattern = pattern.startsWith("!") ? pattern.substring(1) : pattern; lineNumber++; try { - if (GoLangFileMatch.match(pattern, fileName)) { + if (GoLangFileMatch.match(goLangPattern, fileName)) { matches.add(pattern); } } catch (GoLangFileMatchException e) { @@ -233,21 +234,7 @@ private String effectiveMatchingIgnorePattern(File file) { String lastMatchingPattern = matchingPattern.get(matchingPattern.size() - 1); - int lastMatchingPatternIndex = ignores.lastIndexOf(lastMatchingPattern); - - if (lastMatchingPatternIndex == ignores.size() - 1) { - return lastMatchingPattern; - } - - List remainingIgnorePattern = ignores.subList(lastMatchingPatternIndex + 1, ignores.size()); - - for (String ignorePattern : remainingIgnorePattern) { - if (ignorePattern.equals("!" + relativeFilename)) { - return null; - } - } - - return lastMatchingPattern; + return !lastMatchingPattern.startsWith("!") ? lastMatchingPattern : null; } } } diff --git a/src/main/java/com/github/dockerjava/core/exception/GoLangFileMatchException.java b/src/main/java/com/github/dockerjava/core/exception/GoLangFileMatchException.java index 3bbdc2a72..2d92389e7 100644 --- a/src/main/java/com/github/dockerjava/core/exception/GoLangFileMatchException.java +++ b/src/main/java/com/github/dockerjava/core/exception/GoLangFileMatchException.java @@ -7,4 +7,7 @@ public class GoLangFileMatchException extends IllegalArgumentException { private static final long serialVersionUID = -1204971075600864898L; + public GoLangFileMatchException(String s) { + super(s); + } } diff --git a/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java b/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java index 562a954a7..b5a5dcae6 100644 --- a/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java +++ b/src/main/java/com/github/dockerjava/core/util/CertificateUtils.java @@ -2,30 +2,39 @@ import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; 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 java.util.ArrayList; import java.util.List; -import org.apache.commons.io.IOUtils; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.openssl.PEMKeyPair; import org.bouncycastle.openssl.PEMParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.CheckForNull; + +import static java.util.Objects.requireNonNull; public class CertificateUtils { + private static final Logger LOG = LoggerFactory.getLogger(CertificateUtils.class); + private CertificateUtils() { // utility class } @@ -41,115 +50,148 @@ public static boolean verifyCertificatesExist(String dockerCertPath) { return result; } - public static KeyStore createKeyStore(final String dockerCertPath) throws NoSuchAlgorithmException, + @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") + public static KeyStore createKeyStore(final String keypem, final String certpem) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, CertificateException, KeyStoreException { - KeyPair keyPair = loadPrivateKey(dockerCertPath); - List privateCertificates = loadCertificates(dockerCertPath); + PrivateKey privateKey = loadPrivateKey(keypem); + requireNonNull(privateKey); + List privateCertificates = loadCertificates(certpem); KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(null); - keyStore.setKeyEntry("docker", keyPair.getPrivate(), "docker".toCharArray(), - privateCertificates.toArray(new Certificate[privateCertificates.size()])); + keyStore.setKeyEntry("docker", + privateKey, + "docker".toCharArray(), + privateCertificates.toArray(new Certificate[privateCertificates.size()]) + ); + 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); - } + /** + * from "cert.pem" String + */ + public static List loadCertificates(final String certpem) throws IOException, + CertificateException { + final StringReader certReader = new StringReader(certpem); + try (BufferedReader reader = new BufferedReader(certReader)) { + return loadCertificates(reader); } - } - private static List loadCertificates(final String dockerCertPath) throws IOException, + /** + * "cert.pem" from reader + */ + public static List loadCertificates(final Reader reader) throws IOException, CertificateException { - File certificate = new File(dockerCertPath, "cert.pem"); - BufferedReader reader = new BufferedReader(new FileReader(certificate)); - PEMParser pemParser = null; - - try { + try (PEMParser pemParser = new PEMParser(reader)) { List certificates = new ArrayList<>(); - pemParser = new PEMParser(reader); + JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter().setProvider("BC"); Object certObj = pemParser.readObject(); - while (certObj != null) { + if (certObj instanceof X509CertificateHolder) { X509CertificateHolder certificateHolder = (X509CertificateHolder) certObj; certificates.add(certificateConverter.getCertificate(certificateHolder)); - - certObj = pemParser.readObject(); } return certificates; - } 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; + /** + * Return private key ("key.pem") from Reader + */ + @CheckForNull + public static PrivateKey loadPrivateKey(final Reader reader) throws IOException, NoSuchAlgorithmException, + InvalidKeySpecException { + try (PEMParser pemParser = new PEMParser(reader)) { + Object readObject = pemParser.readObject(); + while (readObject != null) { + if (readObject instanceof PEMKeyPair) { + PEMKeyPair pemKeyPair = (PEMKeyPair) readObject; + PrivateKey privateKey = guessKey(pemKeyPair.getPrivateKeyInfo().getEncoded()); + if (privateKey != null) { + return privateKey; + } + } else if (readObject instanceof PrivateKeyInfo) { + PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) readObject; + PrivateKey privateKey = guessKey(privateKeyInfo.getEncoded()); + if (privateKey != null) { + return privateKey; + } + } else if (readObject instanceof ASN1ObjectIdentifier) { + // no idea how it can be used + final ASN1ObjectIdentifier asn1ObjectIdentifier = (ASN1ObjectIdentifier) readObject; + LOG.trace("Ignoring asn1ObjectIdentifier {}", asn1ObjectIdentifier); + } else { + LOG.warn("Unknown object '{}' from PEMParser", readObject); + } + + readObject = pemParser.readObject(); + } + } - try { - pemParser = new PEMParser(reader); + return null; + } - PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject(); + @CheckForNull + public static PrivateKey guessKey(byte[] encodedKey) throws NoSuchAlgorithmException { + //no way to know, so iterate + for (String guessFactory : new String[]{"RSA", "ECDSA"}) { + try { + KeyFactory factory = KeyFactory.getInstance(guessFactory); - byte[] pemPrivateKeyEncoded = pemKeyPair.getPrivateKeyInfo().getEncoded(); - byte[] pemPublicKeyEncoded = pemKeyPair.getPublicKeyInfo().getEncoded(); + PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey); + return factory.generatePrivate(privateKeySpec); + } catch (InvalidKeySpecException ignore) { + } + } - KeyFactory factory = KeyFactory.getInstance("RSA"); + return null; + } - X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pemPublicKeyEncoded); - PublicKey publicKey = factory.generatePublic(publicKeySpec); + /** + * Return KeyPair from "key.pem" + */ + @CheckForNull + public static PrivateKey loadPrivateKey(final String keypem) throws IOException, NoSuchAlgorithmException, + InvalidKeySpecException { + try (StringReader certReader = new StringReader(keypem); + BufferedReader reader = new BufferedReader(certReader)) { + return loadPrivateKey(reader); + } + } - PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pemPrivateKeyEncoded); - PrivateKey privateKey = factory.generatePrivate(privateKeySpec); + /** + * "ca.pem" from String + */ + public static KeyStore createTrustStore(String capem) throws IOException, CertificateException, + KeyStoreException, NoSuchAlgorithmException { + try (Reader certReader = new StringReader(capem)) { + return createTrustStore(certReader); + } + } - return new KeyPair(publicKey, privateKey); + /** + * "ca.pem" from Reader + */ + public static KeyStore createTrustStore(final Reader certReader) throws IOException, CertificateException, + KeyStoreException, NoSuchAlgorithmException { + try (PEMParser pemParser = new PEMParser(certReader)) { + X509CertificateHolder certificateHolder = (X509CertificateHolder) pemParser.readObject(); + Certificate caCertificate = new JcaX509CertificateConverter() + .setProvider("BC") + .getCertificate(certificateHolder); - } finally { - if (pemParser != null) { - IOUtils.closeQuietly(pemParser); - } + KeyStore trustStore = KeyStore.getInstance("JKS"); + trustStore.load(null); + trustStore.setCertificateEntry("ca", caCertificate); - if (reader != null) { - IOUtils.closeQuietly(reader); - } + return trustStore; } - } } diff --git a/src/main/java/com/github/dockerjava/jaxrs/BuildImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/BuildImageCmdExec.java index 43e7687eb..2a7aea40e 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/BuildImageCmdExec.java +++ b/src/main/java/com/github/dockerjava/jaxrs/BuildImageCmdExec.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.net.URLEncoder; +import java.util.Map; public class BuildImageCmdExec extends AbstrAsyncDockerCmdExec implements BuildImageCmd.Exec { @@ -99,19 +100,14 @@ protected AbstractCallbackNotifier callbackNotifier(BuildImag webTarget = webTarget.queryParam("rm", "false"); } - if (command.getBuildArgs() != null && !command.getBuildArgs().isEmpty()) { - try { - webTarget = webTarget.queryParam("buildargs", - URLEncoder.encode(MAPPER.writeValueAsString(command.getBuildArgs()), "UTF-8")); - } catch (IOException e) { - throw new RuntimeException(e); - } - } + webTarget = writeMap(webTarget, "buildargs", command.getBuildArgs()); if (command.getShmsize() != null) { webTarget = webTarget.queryParam("shmsize", command.getShmsize()); } + webTarget = writeMap(webTarget, "labels", command.getLabels()); + webTarget.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.CHUNKED); webTarget.property(ClientProperties.CHUNKED_ENCODING_SIZE, 1024 * 1024); @@ -123,4 +119,17 @@ protected AbstractCallbackNotifier callbackNotifier(BuildImag entity(command.getTarInputStream(), "application/tar") ); } + + private WebTarget writeMap(WebTarget webTarget, String name, Map value) { + if (value != null && !value.isEmpty()) { + try { + return webTarget.queryParam(name, + URLEncoder.encode(MAPPER.writeValueAsString(value), "UTF-8")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + return webTarget; + } + } } diff --git a/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java index 5d489bbc5..1d407e233 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java +++ b/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java @@ -1,568 +1,9 @@ package com.github.dockerjava.jaxrs; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.ProxySelector; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; - -import javax.net.ssl.SSLContext; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.ClientRequestFilter; -import javax.ws.rs.client.ClientResponseFilter; -import javax.ws.rs.client.WebTarget; - -import com.github.dockerjava.api.command.UpdateContainerCmd; -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.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; -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.ConnectToNetworkCmd; -import com.github.dockerjava.api.command.ContainerDiffCmd; -import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd; -import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; -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.CreateNetworkCmd; -import com.github.dockerjava.api.command.CreateVolumeCmd; -import com.github.dockerjava.api.command.DisconnectFromNetworkCmd; -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.InspectNetworkCmd; -import com.github.dockerjava.api.command.InspectVolumeCmd; -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.ListNetworksCmd; -import com.github.dockerjava.api.command.ListVolumesCmd; -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.RemoveNetworkCmd; -import com.github.dockerjava.api.command.RemoveVolumeCmd; -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; -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.api.command.RenameContainerCmd; -import com.github.dockerjava.api.exception.DockerClientException; -import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.core.LocalDirectorySSLConfig; -import com.github.dockerjava.jaxrs.connector.ApacheConnectorProvider; -import com.github.dockerjava.jaxrs.filter.JsonClientFilter; -import com.github.dockerjava.jaxrs.filter.ResponseStatusExceptionFilter; -import com.github.dockerjava.jaxrs.filter.SelectiveLoggingFilter; - -//import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; -// see https://github.com/docker-java/docker-java/issues/196 - -public class DockerCmdExecFactoryImpl implements DockerCmdExecFactory { - - private static final Logger LOGGER = LoggerFactory.getLogger(DockerCmdExecFactoryImpl.class.getName()); - - private Client client; - - private WebTarget baseResource; - - private Integer readTimeout = null; - - private Integer connectTimeout = null; - - private Integer maxTotalConnections = null; - - private Integer maxPerRouteConnections = null; - - private ClientRequestFilter[] clientRequestFilters = null; - - private ClientResponseFilter[] clientResponseFilters = null; - - private DockerClientConfig dockerClientConfig; - - SSLContext sslContext = null; - - @Override - public void init(DockerClientConfig dockerClientConfig) { - checkNotNull(dockerClientConfig, "config was not specified"); - this.dockerClientConfig = dockerClientConfig; - - ClientConfig clientConfig = new ClientConfig(); - clientConfig.connectorProvider(new ApacheConnectorProvider()); - clientConfig.property(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true); - - clientConfig.register(ResponseStatusExceptionFilter.class); - clientConfig.register(JsonClientFilter.class); - clientConfig.register(JacksonJsonProvider.class); - - // logging may disabled via log level - clientConfig.register(new SelectiveLoggingFilter(LOGGER, true)); - - if (readTimeout != null) { - clientConfig.property(ClientProperties.READ_TIMEOUT, readTimeout); - } - - if (connectTimeout != null) { - clientConfig.property(ClientProperties.CONNECT_TIMEOUT, connectTimeout); - } - - if (clientResponseFilters != null) { - for (ClientResponseFilter clientResponseFilter : clientResponseFilters) { - if (clientResponseFilter != null) { - clientConfig.register(clientResponseFilter); - } - } - } - - if (clientRequestFilters != null) { - for (ClientRequestFilter clientRequestFilter : clientRequestFilters) { - if (clientRequestFilter != null) { - clientConfig.register(clientRequestFilter); - } - } - } - - URI originalUri = dockerClientConfig.getDockerHost(); - - String protocol = null; - - if (dockerClientConfig.getDockerTlsVerify()) { - protocol = "https"; - - try { - - if (sslContext == null) { - sslContext = new LocalDirectorySSLConfig(dockerClientConfig.getDockerCertPath()).getSSLContext(); - } - - } catch (Exception ex) { - throw new DockerClientException("Error in SSL Configuration", ex); - } - } else { - protocol = "http"; - } - - if (!originalUri.getScheme().equals("unix")) { - - try { - originalUri = new URI(originalUri.toString().replaceFirst("tcp", protocol)); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - configureProxy(clientConfig, protocol); - } - - PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(getSchemeRegistry( - originalUri, sslContext)); - - if (maxTotalConnections != null) { - connManager.setMaxTotal(maxTotalConnections); - } - if (maxPerRouteConnections != null) { - connManager.setDefaultMaxPerRoute(maxPerRouteConnections); - } - - clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, connManager); - - // Configure connection pool timeout - // clientConfig.property(ApacheClientProperties.REQUEST_CONFIG, RequestConfig.custom() - // .setConnectionRequestTimeout(1000).build()); - - ClientBuilder clientBuilder = ClientBuilder.newBuilder().withConfig(clientConfig); - - if (sslContext != null) { - clientBuilder.sslContext(sslContext); - } - - client = clientBuilder.build(); - - baseResource = client.target(sanitizeUrl(originalUri).toString()).path(dockerClientConfig.getApiVersion().asWebPathPart()); - } - - private URI sanitizeUrl(URI originalUri) { - if (originalUri.getScheme().equals("unix")) { - return UnixConnectionSocketFactory.sanitizeUri(originalUri); - } - return originalUri; - } - - private void configureProxy(ClientConfig clientConfig, String protocol) { - - List proxies = ProxySelector.getDefault().select(dockerClientConfig.getDockerHost()); - - for (Proxy proxy : proxies) { - InetSocketAddress address = (InetSocketAddress) proxy.address(); - if (address != null) { - String hostname = address.getHostName(); - int port = address.getPort(); - - clientConfig.property(ClientProperties.PROXY_URI, "http://" + hostname + ":" + port); - - String httpProxyUser = System.getProperty(protocol + ".proxyUser"); - if (httpProxyUser != null) { - clientConfig.property(ClientProperties.PROXY_USERNAME, httpProxyUser); - String httpProxyPassword = System.getProperty(protocol + ".proxyPassword"); - if (httpProxyPassword != null) { - clientConfig.property(ClientProperties.PROXY_PASSWORD, httpProxyPassword); - } - } - } - } - } - - 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, baseResource not set. You probably forgot to call init()!"); - return baseResource; - } - - protected DockerClientConfig getDockerClientConfig() { - checkNotNull(dockerClientConfig, - "Factor not initialized, dockerClientConfig not set. You probably forgot to call init()!"); - return dockerClientConfig; - } - - @Override - public AuthCmd.Exec createAuthCmdExec() { - return new AuthCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InfoCmd.Exec createInfoCmdExec() { - return new InfoCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public PingCmd.Exec createPingCmdExec() { - return new PingCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public VersionCmd.Exec createVersionCmdExec() { - return new VersionCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public PullImageCmd.Exec createPullImageCmdExec() { - return new PullImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public PushImageCmd.Exec createPushImageCmdExec() { - return new PushImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public SaveImageCmd.Exec createSaveImageCmdExec() { - return new SaveImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CreateImageCmd.Exec createCreateImageCmdExec() { - return new CreateImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public SearchImagesCmd.Exec createSearchImagesCmdExec() { - return new SearchImagesCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RemoveImageCmd.Exec createRemoveImageCmdExec() { - return new RemoveImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ListImagesCmd.Exec createListImagesCmdExec() { - return new ListImagesCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InspectImageCmd.Exec createInspectImageCmdExec() { - return new InspectImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ListContainersCmd.Exec createListContainersCmdExec() { - return new ListContainersCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CreateContainerCmd.Exec createCreateContainerCmdExec() { - return new CreateContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public StartContainerCmd.Exec createStartContainerCmdExec() { - return new StartContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InspectContainerCmd.Exec createInspectContainerCmdExec() { - return new InspectContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ExecCreateCmd.Exec createExecCmdExec() { - return new ExecCreateCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RemoveContainerCmd.Exec createRemoveContainerCmdExec() { - return new RemoveContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public WaitContainerCmd.Exec createWaitContainerCmdExec() { - return new WaitContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public AttachContainerCmd.Exec createAttachContainerCmdExec() { - return new AttachContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ExecStartCmd.Exec createExecStartCmdExec() { - return new ExecStartCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InspectExecCmd.Exec createInspectExecCmdExec() { - return new InspectExecCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public LogContainerCmd.Exec createLogContainerCmdExec() { - return new LogContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CopyArchiveFromContainerCmd.Exec createCopyArchiveFromContainerCmdExec() { - return new CopyArchiveFromContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec() { - return new CopyFileFromContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CopyArchiveToContainerCmd.Exec createCopyArchiveToContainerCmdExec() { - return new CopyArchiveToContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public StopContainerCmd.Exec createStopContainerCmdExec() { - return new StopContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ContainerDiffCmd.Exec createContainerDiffCmdExec() { - return new ContainerDiffCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public KillContainerCmd.Exec createKillContainerCmdExec() { - return new KillContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - - @Override - public UpdateContainerCmd.Exec createUpdateContainerCmdExec() { - return new UpdateContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RenameContainerCmd.Exec createRenameContainerCmdExec() { - return new RenameContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RestartContainerCmd.Exec createRestartContainerCmdExec() { - return new RestartContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CommitCmd.Exec createCommitCmdExec() { - return new CommitCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public BuildImageCmd.Exec createBuildImageCmdExec() { - return new BuildImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public TopContainerCmd.Exec createTopContainerCmdExec() { - return new TopContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public TagImageCmd.Exec createTagImageCmdExec() { - return new TagImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public PauseContainerCmd.Exec createPauseContainerCmdExec() { - return new PauseContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec() { - return new UnpauseContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public EventsCmd.Exec createEventsCmdExec() { - return new EventsCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public StatsCmd.Exec createStatsCmdExec() { - return new StatsCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CreateVolumeCmd.Exec createCreateVolumeCmdExec() { - return new CreateVolumeCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InspectVolumeCmd.Exec createInspectVolumeCmdExec() { - return new InspectVolumeCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RemoveVolumeCmd.Exec createRemoveVolumeCmdExec() { - return new RemoveVolumeCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ListVolumesCmd.Exec createListVolumesCmdExec() { - return new ListVolumesCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ListNetworksCmd.Exec createListNetworksCmdExec() { - return new ListNetworksCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InspectNetworkCmd.Exec createInspectNetworkCmdExec() { - - return new InspectNetworkCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CreateNetworkCmd.Exec createCreateNetworkCmdExec() { - - return new CreateNetworkCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RemoveNetworkCmd.Exec createRemoveNetworkCmdExec() { - - return new RemoveNetworkCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ConnectToNetworkCmd.Exec createConnectToNetworkCmdExec() { - - return new ConnectToNetworkCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public DisconnectFromNetworkCmd.Exec createDisconnectFromNetworkCmdExec() { - - return new DisconnectFromNetworkCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public void close() throws IOException { - checkNotNull(client, "Factory not initialized. You probably forgot to call init()!"); - client.close(); - } - - @Override - public DockerCmdExecFactoryImpl withSSLContext(SSLContext sslContext) { - this.sslContext = sslContext; - return this; - } - - public DockerCmdExecFactoryImpl withReadTimeout(Integer readTimeout) { - this.readTimeout = readTimeout; - return this; - } - - public DockerCmdExecFactoryImpl withConnectTimeout(Integer connectTimeout) { - this.connectTimeout = connectTimeout; - return this; - } - - public DockerCmdExecFactoryImpl withMaxTotalConnections(Integer maxTotalConnections) { - this.maxTotalConnections = maxTotalConnections; - return this; - } - - public DockerCmdExecFactoryImpl withMaxPerRouteConnections(Integer maxPerRouteConnections) { - this.maxPerRouteConnections = maxPerRouteConnections; - return this; - } - - public DockerCmdExecFactoryImpl withClientResponseFilters(ClientResponseFilter... clientResponseFilter) { - this.clientResponseFilters = clientResponseFilter; - return this; - } - - public DockerCmdExecFactoryImpl withClientRequestFilters(ClientRequestFilter... clientRequestFilters) { - this.clientRequestFilters = clientRequestFilters; - return this; - } - +/** + * @author Kanstantsin Shautsou + * @deprecated clashes with netty impl. + */ +@Deprecated +public class DockerCmdExecFactoryImpl extends JerseyDockerCmdExecFactory { } diff --git a/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerCmdExecFactory.java b/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerCmdExecFactory.java new file mode 100644 index 000000000..73bab979e --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/JerseyDockerCmdExecFactory.java @@ -0,0 +1,592 @@ +package com.github.dockerjava.jaxrs; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import javax.net.ssl.SSLContext; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.client.ClientResponseFilter; +import javax.ws.rs.client.WebTarget; + +import com.github.dockerjava.api.command.UpdateContainerCmd; +import com.github.dockerjava.core.SSLConfig; + +import org.apache.http.client.config.RequestConfig; +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.apache.connector.ApacheConnectorProvider; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +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.ConnectToNetworkCmd; +import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd; +import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; +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.CreateNetworkCmd; +import com.github.dockerjava.api.command.CreateVolumeCmd; +import com.github.dockerjava.api.command.DisconnectFromNetworkCmd; +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.InspectNetworkCmd; +import com.github.dockerjava.api.command.InspectVolumeCmd; +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.ListNetworksCmd; +import com.github.dockerjava.api.command.ListVolumesCmd; +import com.github.dockerjava.api.command.LoadImageCmd; +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.RemoveNetworkCmd; +import com.github.dockerjava.api.command.RemoveVolumeCmd; +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; +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.api.command.RenameContainerCmd; +import com.github.dockerjava.api.exception.DockerClientException; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.jaxrs.filter.JsonClientFilter; +import com.github.dockerjava.jaxrs.filter.ResponseStatusExceptionFilter; +import com.github.dockerjava.jaxrs.filter.SelectiveLoggingFilter; + +//import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; +// see https://github.com/docker-java/docker-java/issues/196 + +public class JerseyDockerCmdExecFactory implements DockerCmdExecFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(JerseyDockerCmdExecFactory.class.getName()); + + private Client client; + + private WebTarget baseResource; + + private Integer readTimeout = null; + + private Integer connectTimeout = null; + + private Integer maxTotalConnections = null; + + private Integer maxPerRouteConnections = null; + + private Integer connectionRequestTimeout = null; + + private ClientRequestFilter[] clientRequestFilters = null; + + private ClientResponseFilter[] clientResponseFilters = null; + + private DockerClientConfig dockerClientConfig; + + private PoolingHttpClientConnectionManager connManager = null; + + @Override + public void init(DockerClientConfig dockerClientConfig) { + checkNotNull(dockerClientConfig, "config was not specified"); + this.dockerClientConfig = dockerClientConfig; + + ClientConfig clientConfig = new ClientConfig(); + clientConfig.connectorProvider(new ApacheConnectorProvider()); + clientConfig.property(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true); + + clientConfig.register(ResponseStatusExceptionFilter.class); + clientConfig.register(JsonClientFilter.class); + clientConfig.register(JacksonJsonProvider.class); + + // logging may disabled via log level + clientConfig.register(new SelectiveLoggingFilter(LOGGER, true)); + + if (readTimeout != null) { + clientConfig.property(ClientProperties.READ_TIMEOUT, readTimeout); + } + + if (connectTimeout != null) { + clientConfig.property(ClientProperties.CONNECT_TIMEOUT, connectTimeout); + } + + if (clientResponseFilters != null) { + for (ClientResponseFilter clientResponseFilter : clientResponseFilters) { + if (clientResponseFilter != null) { + clientConfig.register(clientResponseFilter); + } + } + } + + if (clientRequestFilters != null) { + for (ClientRequestFilter clientRequestFilter : clientRequestFilters) { + if (clientRequestFilter != null) { + clientConfig.register(clientRequestFilter); + } + } + } + + URI originalUri = dockerClientConfig.getDockerHost(); + + String protocol = null; + + SSLContext sslContext = null; + + try { + final SSLConfig sslConfig = dockerClientConfig.getSSLConfig(); + if (sslConfig != null) { + sslContext = sslConfig.getSSLContext(); + } + } catch (Exception ex) { + throw new DockerClientException("Error in SSL Configuration", ex); + } + + if (sslContext != null) { + protocol = "https"; + } else { + protocol = "http"; + } + + if (!originalUri.getScheme().equals("unix")) { + + try { + originalUri = new URI(originalUri.toString().replaceFirst("tcp", protocol)); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + configureProxy(clientConfig, protocol); + } + + connManager = new PoolingHttpClientConnectionManager(getSchemeRegistry( + originalUri, sslContext)) { + + @Override + public void close() { + super.shutdown(); + } + + @Override + public void shutdown() { + // Disable shutdown of the pool. This will be done later, when this factory is closed + // This is a workaround for finalize method on jerseys ClientRuntime which + // closes the client and shuts down the connection pool when it is garbage collected + } + }; + + if (maxTotalConnections != null) { + connManager.setMaxTotal(maxTotalConnections); + } + if (maxPerRouteConnections != null) { + connManager.setDefaultMaxPerRoute(maxPerRouteConnections); + } + + clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, connManager); + + // Configure connection pool timeout + if (connectionRequestTimeout != null) { + clientConfig.property(ApacheClientProperties.REQUEST_CONFIG, RequestConfig.custom() + .setConnectionRequestTimeout(connectionRequestTimeout).build()); + } + ClientBuilder clientBuilder = ClientBuilder.newBuilder().withConfig(clientConfig); + + if (sslContext != null) { + clientBuilder.sslContext(sslContext); + } + + client = clientBuilder.build(); + + baseResource = client.target(sanitizeUrl(originalUri).toString()).path(dockerClientConfig.getApiVersion().asWebPathPart()); + } + + private URI sanitizeUrl(URI originalUri) { + if (originalUri.getScheme().equals("unix")) { + return UnixConnectionSocketFactory.sanitizeUri(originalUri); + } + return originalUri; + } + + private void configureProxy(ClientConfig clientConfig, String protocol) { + + List proxies = ProxySelector.getDefault().select(dockerClientConfig.getDockerHost()); + + for (Proxy proxy : proxies) { + InetSocketAddress address = (InetSocketAddress) proxy.address(); + if (address != null) { + String hostname = address.getHostName(); + int port = address.getPort(); + + clientConfig.property(ClientProperties.PROXY_URI, "http://" + hostname + ":" + port); + + String httpProxyUser = System.getProperty(protocol + ".proxyUser"); + if (httpProxyUser != null) { + clientConfig.property(ClientProperties.PROXY_USERNAME, httpProxyUser); + String httpProxyPassword = System.getProperty(protocol + ".proxyPassword"); + if (httpProxyPassword != null) { + clientConfig.property(ClientProperties.PROXY_PASSWORD, httpProxyPassword); + } + } + } + } + } + + 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, baseResource not set. You probably forgot to call init()!"); + return baseResource; + } + + protected DockerClientConfig getDockerClientConfig() { + checkNotNull(dockerClientConfig, + "Factor not initialized, dockerClientConfig not set. You probably forgot to call init()!"); + return dockerClientConfig; + } + + @Override + public AuthCmd.Exec createAuthCmdExec() { + return new AuthCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InfoCmd.Exec createInfoCmdExec() { + return new InfoCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public PingCmd.Exec createPingCmdExec() { + return new PingCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public VersionCmd.Exec createVersionCmdExec() { + return new VersionCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public PullImageCmd.Exec createPullImageCmdExec() { + return new PullImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public PushImageCmd.Exec createPushImageCmdExec() { + return new PushImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public SaveImageCmd.Exec createSaveImageCmdExec() { + return new SaveImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CreateImageCmd.Exec createCreateImageCmdExec() { + return new CreateImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public LoadImageCmd.Exec createLoadImageCmdExec() { + return new LoadImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public SearchImagesCmd.Exec createSearchImagesCmdExec() { + return new SearchImagesCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RemoveImageCmd.Exec createRemoveImageCmdExec() { + return new RemoveImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ListImagesCmd.Exec createListImagesCmdExec() { + return new ListImagesCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InspectImageCmd.Exec createInspectImageCmdExec() { + return new InspectImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ListContainersCmd.Exec createListContainersCmdExec() { + return new ListContainersCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CreateContainerCmd.Exec createCreateContainerCmdExec() { + return new CreateContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public StartContainerCmd.Exec createStartContainerCmdExec() { + return new StartContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InspectContainerCmd.Exec createInspectContainerCmdExec() { + return new InspectContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ExecCreateCmd.Exec createExecCmdExec() { + return new ExecCreateCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RemoveContainerCmd.Exec createRemoveContainerCmdExec() { + return new RemoveContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public WaitContainerCmd.Exec createWaitContainerCmdExec() { + return new WaitContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public AttachContainerCmd.Exec createAttachContainerCmdExec() { + return new AttachContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ExecStartCmd.Exec createExecStartCmdExec() { + return new ExecStartCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InspectExecCmd.Exec createInspectExecCmdExec() { + return new InspectExecCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public LogContainerCmd.Exec createLogContainerCmdExec() { + return new LogContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CopyArchiveFromContainerCmd.Exec createCopyArchiveFromContainerCmdExec() { + return new CopyArchiveFromContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec() { + return new CopyFileFromContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CopyArchiveToContainerCmd.Exec createCopyArchiveToContainerCmdExec() { + return new CopyArchiveToContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public StopContainerCmd.Exec createStopContainerCmdExec() { + return new StopContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ContainerDiffCmd.Exec createContainerDiffCmdExec() { + return new ContainerDiffCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public KillContainerCmd.Exec createKillContainerCmdExec() { + return new KillContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public UpdateContainerCmd.Exec createUpdateContainerCmdExec() { + return new UpdateContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RenameContainerCmd.Exec createRenameContainerCmdExec() { + return new RenameContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RestartContainerCmd.Exec createRestartContainerCmdExec() { + return new RestartContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CommitCmd.Exec createCommitCmdExec() { + return new CommitCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public BuildImageCmd.Exec createBuildImageCmdExec() { + return new BuildImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public TopContainerCmd.Exec createTopContainerCmdExec() { + return new TopContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public TagImageCmd.Exec createTagImageCmdExec() { + return new TagImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public PauseContainerCmd.Exec createPauseContainerCmdExec() { + return new PauseContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec() { + return new UnpauseContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public EventsCmd.Exec createEventsCmdExec() { + return new EventsCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public StatsCmd.Exec createStatsCmdExec() { + return new StatsCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CreateVolumeCmd.Exec createCreateVolumeCmdExec() { + return new CreateVolumeCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InspectVolumeCmd.Exec createInspectVolumeCmdExec() { + return new InspectVolumeCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RemoveVolumeCmd.Exec createRemoveVolumeCmdExec() { + return new RemoveVolumeCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ListVolumesCmd.Exec createListVolumesCmdExec() { + return new ListVolumesCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ListNetworksCmd.Exec createListNetworksCmdExec() { + return new ListNetworksCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InspectNetworkCmd.Exec createInspectNetworkCmdExec() { + + return new InspectNetworkCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CreateNetworkCmd.Exec createCreateNetworkCmdExec() { + + return new CreateNetworkCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RemoveNetworkCmd.Exec createRemoveNetworkCmdExec() { + + return new RemoveNetworkCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ConnectToNetworkCmd.Exec createConnectToNetworkCmdExec() { + + return new ConnectToNetworkCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public DisconnectFromNetworkCmd.Exec createDisconnectFromNetworkCmdExec() { + + return new DisconnectFromNetworkCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public void close() throws IOException { + checkNotNull(client, "Factory not initialized. You probably forgot to call init()!"); + client.close(); + connManager.close(); + } + + public JerseyDockerCmdExecFactory withReadTimeout(Integer readTimeout) { + this.readTimeout = readTimeout; + return this; + } + + public JerseyDockerCmdExecFactory withConnectTimeout(Integer connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + public JerseyDockerCmdExecFactory withMaxTotalConnections(Integer maxTotalConnections) { + this.maxTotalConnections = maxTotalConnections; + return this; + } + + public JerseyDockerCmdExecFactory withMaxPerRouteConnections(Integer maxPerRouteConnections) { + this.maxPerRouteConnections = maxPerRouteConnections; + return this; + } + + public JerseyDockerCmdExecFactory withConnectionRequestTimeout(Integer connectionRequestTimeout) { + this.connectionRequestTimeout = connectionRequestTimeout; + return this; + } + + public JerseyDockerCmdExecFactory withClientResponseFilters(ClientResponseFilter... clientResponseFilter) { + this.clientResponseFilters = clientResponseFilter; + return this; + } + + public JerseyDockerCmdExecFactory withClientRequestFilters(ClientRequestFilter... clientRequestFilters) { + this.clientRequestFilters = clientRequestFilters; + return this; + } + +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/LoadImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/LoadImageCmdExec.java new file mode 100644 index 000000000..23d6c493a --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/LoadImageCmdExec.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.LoadImageCmd; +import com.github.dockerjava.core.DockerClientConfig; + +public class LoadImageCmdExec extends AbstrSyncDockerCmdExec implements LoadImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(LoadImageCmdExec.class); + + public LoadImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(LoadImageCmd command) { + WebTarget webTarget = getBaseResource().path("/images/load"); + + LOGGER.trace("POST: {}", webTarget); + webTarget.request().post(entity(command.getImageStream(), MediaType.APPLICATION_OCTET_STREAM)); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/StartContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/StartContainerCmdExec.java index 0f75c4f2f..2851a26c7 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/StartContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/jaxrs/StartContainerCmdExec.java @@ -1,7 +1,5 @@ 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; @@ -22,11 +20,13 @@ public StartContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerCl @Override protected Void execute(StartContainerCmd command) { - WebTarget webResource = getBaseResource().path("/containers/{id}/start").resolveTemplate("id", - command.getContainerId()); + 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)) + webResource.request() + .accept(MediaType.APPLICATION_JSON) + .post(null) .close(); return null; diff --git a/src/main/java/com/github/dockerjava/jaxrs/UnixConnectionSocketFactory.java b/src/main/java/com/github/dockerjava/jaxrs/UnixConnectionSocketFactory.java index 870e2fd04..0c5400ecd 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/UnixConnectionSocketFactory.java +++ b/src/main/java/com/github/dockerjava/jaxrs/UnixConnectionSocketFactory.java @@ -29,7 +29,8 @@ import java.net.URI; import org.apache.http.HttpHost; -import org.apache.http.annotation.Immutable; +import org.apache.http.annotation.Contract; +import org.apache.http.annotation.ThreadingBehavior; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.protocol.HttpContext; @@ -38,7 +39,7 @@ /** * Provides a ConnectionSocketFactory for connecting Apache HTTP clients to Unix sockets. */ -@Immutable +@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class UnixConnectionSocketFactory implements ConnectionSocketFactory { private File socketFile; diff --git a/src/main/java/com/github/dockerjava/jaxrs/async/AbstractCallbackNotifier.java b/src/main/java/com/github/dockerjava/jaxrs/async/AbstractCallbackNotifier.java index 86f528af6..cd7a7f809 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/async/AbstractCallbackNotifier.java +++ b/src/main/java/com/github/dockerjava/jaxrs/async/AbstractCallbackNotifier.java @@ -60,8 +60,7 @@ public Void call() throws Exception { return null; } - try { - InputStream inputStream = new WrappedResponseInputStream(response); + try (InputStream inputStream = new WrappedResponseInputStream(response)) { if (resultCallback != null) { responseStreamProcessor.processResponseStream(inputStream, resultCallback); diff --git a/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnector.java b/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnector.java deleted file mode 100644 index 68b47bfa8..000000000 --- a/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnector.java +++ /dev/null @@ -1,663 +0,0 @@ -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.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -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 jersey.repackaged.com.google.common.util.concurrent.MoreExecutors; - -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 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; - -/** - * 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 static final Logger LOGGER = Logger.getLogger(ApacheConnector.class.getName()); - - private static final VersionInfo VERSION_INFO; - - private static final String RELEASE; - - static { - VERSION_INFO = VersionInfo.loadVersionInfo("org.apache.http.client", HttpClientBuilder.class.getClassLoader()); - RELEASE = (VERSION_INFO != null) ? VERSION_INFO.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 && !(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); - } - - // context.setRequestConfig(RequestConfig.custom().setConnectionRequestTimeout(10).build()); - - 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 deleted file mode 100644 index fead3575c..000000000 --- a/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorClientResponse.java +++ /dev/null @@ -1,48 +0,0 @@ -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 Linke - * - */ -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 deleted file mode 100644 index eafb55c29..000000000 --- a/src/main/java/com/github/dockerjava/jaxrs/connector/ApacheConnectorProvider.java +++ /dev/null @@ -1,146 +0,0 @@ -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.apache.http.client.HttpClient; -import org.glassfish.jersey.apache.connector.ApacheClientProperties; -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; - -/** - * 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 deleted file mode 100644 index c3c1415f1..000000000 --- a/src/main/java/com/github/dockerjava/jaxrs/connector/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -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 index afe0dce92..4ec74ddcc 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/util/WrappedResponseInputStream.java +++ b/src/main/java/com/github/dockerjava/jaxrs/util/WrappedResponseInputStream.java @@ -53,9 +53,12 @@ public int available() throws IOException { } public void close() throws IOException { + if (closed) { + return; + } closed = true; - response.close(); delegate.close(); + response.close(); } public void mark(int readlimit) { diff --git a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java index 8f704c7af..25825d169 100644 --- a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java +++ b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java @@ -1,589 +1,9 @@ package com.github.dockerjava.netty; -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.ConnectToNetworkCmd; -import com.github.dockerjava.api.command.ContainerDiffCmd; -import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd; -import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; -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.CreateNetworkCmd; -import com.github.dockerjava.api.command.CreateVolumeCmd; -import com.github.dockerjava.api.command.DisconnectFromNetworkCmd; -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.InspectNetworkCmd; -import com.github.dockerjava.api.command.InspectVolumeCmd; -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.ListNetworksCmd; -import com.github.dockerjava.api.command.ListVolumesCmd; -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.RemoveNetworkCmd; -import com.github.dockerjava.api.command.RemoveVolumeCmd; -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; -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.UpdateContainerCmd; -import com.github.dockerjava.api.command.VersionCmd; -import com.github.dockerjava.api.command.WaitContainerCmd; -import com.github.dockerjava.api.command.RenameContainerCmd; -import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.core.DockerClientImpl; -import com.github.dockerjava.core.LocalDirectorySSLConfig; -import com.github.dockerjava.netty.exec.AttachContainerCmdExec; -import com.github.dockerjava.netty.exec.AuthCmdExec; -import com.github.dockerjava.netty.exec.BuildImageCmdExec; -import com.github.dockerjava.netty.exec.CommitCmdExec; -import com.github.dockerjava.netty.exec.ConnectToNetworkCmdExec; -import com.github.dockerjava.netty.exec.ContainerDiffCmdExec; -import com.github.dockerjava.netty.exec.CopyArchiveFromContainerCmdExec; -import com.github.dockerjava.netty.exec.CopyArchiveToContainerCmdExec; -import com.github.dockerjava.netty.exec.CopyFileFromContainerCmdExec; -import com.github.dockerjava.netty.exec.CreateContainerCmdExec; -import com.github.dockerjava.netty.exec.CreateImageCmdExec; -import com.github.dockerjava.netty.exec.CreateNetworkCmdExec; -import com.github.dockerjava.netty.exec.CreateVolumeCmdExec; -import com.github.dockerjava.netty.exec.DisconnectFromNetworkCmdExec; -import com.github.dockerjava.netty.exec.EventsCmdExec; -import com.github.dockerjava.netty.exec.ExecCreateCmdExec; -import com.github.dockerjava.netty.exec.ExecStartCmdExec; -import com.github.dockerjava.netty.exec.InfoCmdExec; -import com.github.dockerjava.netty.exec.InspectContainerCmdExec; -import com.github.dockerjava.netty.exec.InspectExecCmdExec; -import com.github.dockerjava.netty.exec.InspectImageCmdExec; -import com.github.dockerjava.netty.exec.InspectNetworkCmdExec; -import com.github.dockerjava.netty.exec.InspectVolumeCmdExec; -import com.github.dockerjava.netty.exec.KillContainerCmdExec; -import com.github.dockerjava.netty.exec.ListContainersCmdExec; -import com.github.dockerjava.netty.exec.ListImagesCmdExec; -import com.github.dockerjava.netty.exec.ListNetworksCmdExec; -import com.github.dockerjava.netty.exec.ListVolumesCmdExec; -import com.github.dockerjava.netty.exec.LogContainerCmdExec; -import com.github.dockerjava.netty.exec.PauseContainerCmdExec; -import com.github.dockerjava.netty.exec.PingCmdExec; -import com.github.dockerjava.netty.exec.PullImageCmdExec; -import com.github.dockerjava.netty.exec.PushImageCmdExec; -import com.github.dockerjava.netty.exec.RemoveContainerCmdExec; -import com.github.dockerjava.netty.exec.RemoveImageCmdExec; -import com.github.dockerjava.netty.exec.RemoveNetworkCmdExec; -import com.github.dockerjava.netty.exec.RemoveVolumeCmdExec; -import com.github.dockerjava.netty.exec.RestartContainerCmdExec; -import com.github.dockerjava.netty.exec.SaveImageCmdExec; -import com.github.dockerjava.netty.exec.SearchImagesCmdExec; -import com.github.dockerjava.netty.exec.StartContainerCmdExec; -import com.github.dockerjava.netty.exec.StatsCmdExec; -import com.github.dockerjava.netty.exec.StopContainerCmdExec; -import com.github.dockerjava.netty.exec.TagImageCmdExec; -import com.github.dockerjava.netty.exec.TopContainerCmdExec; -import com.github.dockerjava.netty.exec.UnpauseContainerCmdExec; -import com.github.dockerjava.netty.exec.UpdateContainerCmdExec; -import com.github.dockerjava.netty.exec.VersionCmdExec; -import com.github.dockerjava.netty.exec.WaitContainerCmdExec; -import com.github.dockerjava.netty.exec.RenameContainerCmdExec; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.epoll.EpollDomainSocketChannel; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.DuplexChannel; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.channel.unix.DomainSocketAddress; -import io.netty.channel.unix.UnixChannel; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslHandler; -import io.netty.util.concurrent.DefaultThreadFactory; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.security.Security; - -import static com.google.common.base.Preconditions.checkNotNull; - /** - * Experimental implementation of {@link DockerCmdExecFactory} that supports http connection hijacking that is needed to pass STDIN to the - * container. - * - * To use it just pass an instance via {@link DockerClientImpl#withDockerCmdExecFactory(DockerCmdExecFactory)} - * - * @see https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/#attach-to-a-container - * @see https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/#exec-start - * - * - * @author Marcus Linke + * @author Kanstantsin Shautsou + * @deprecated old clashing name */ -public class DockerCmdExecFactoryImpl implements DockerCmdExecFactory { - - private static String threadPrefix = "dockerjava-netty"; - - /* - * useful links: - * - * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain-socket-failed - * http://netty.io/wiki/native-transports.html - * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java - * https://github.com/slandelle/netty-request-chunking/blob/master/src/test/java/slandelle/ChunkingTest.java - */ - - private DockerClientConfig dockerClientConfig; - - private Bootstrap bootstrap; - - private EventLoopGroup eventLoopGroup; - - private NettyInitializer nettyInitializer; - - private SSLContext sslContext = null; - - private ChannelProvider channelProvider = new ChannelProvider() { - @Override - public DuplexChannel getChannel() { - DuplexChannel channel = connect(); - channel.pipeline().addLast(new LoggingHandler(getClass())); - return channel; - } - }; - - @Override - public void init(DockerClientConfig dockerClientConfig) { - checkNotNull(dockerClientConfig, "config was not specified"); - this.dockerClientConfig = dockerClientConfig; - - bootstrap = new Bootstrap(); - - String scheme = dockerClientConfig.getDockerHost().getScheme(); - - if ("unix".equals(scheme)) { - nettyInitializer = new UnixDomainSocketInitializer(); - } else if ("tcp".equals(scheme)) { - nettyInitializer = new InetSocketInitializer(); - } - - eventLoopGroup = nettyInitializer.init(bootstrap, dockerClientConfig); - } - - private DuplexChannel connect() { - try { - return connect(bootstrap); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - private DuplexChannel connect(final Bootstrap bootstrap) throws InterruptedException { - return nettyInitializer.connect(bootstrap); - } - - private interface NettyInitializer { - EventLoopGroup init(final Bootstrap bootstrap, DockerClientConfig dockerClientConfig); - - DuplexChannel connect(final Bootstrap bootstrap) throws InterruptedException; - } - - private class UnixDomainSocketInitializer implements NettyInitializer { - @Override - public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { - EventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(0, new DefaultThreadFactory(threadPrefix)); - bootstrap.group(epollEventLoopGroup).channel(EpollDomainSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(final UnixChannel channel) throws Exception { - channel.pipeline().addLast(new HttpClientCodec()); - } - }); - return epollEventLoopGroup; - } - - @Override - public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { - return (DuplexChannel) bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync().channel(); - } - } - - private class InetSocketInitializer implements NettyInitializer { - @Override - public EventLoopGroup init(Bootstrap bootstrap, final DockerClientConfig dockerClientConfig) { - EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(0, new DefaultThreadFactory(threadPrefix)); - - InetAddress addr = InetAddress.getLoopbackAddress(); - - final SocketAddress proxyAddress = new InetSocketAddress(addr, 8008); - - Security.addProvider(new BouncyCastleProvider()); - - bootstrap.group(nioEventLoopGroup).channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(final SocketChannel channel) throws Exception { - // channel.pipeline().addLast(new - // HttpProxyHandler(proxyAddress)); - channel.pipeline().addLast(new HttpClientCodec()); - } - }); - - return nioEventLoopGroup; - } - - @Override - public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { - String host = dockerClientConfig.getDockerHost().getHost(); - int port = dockerClientConfig.getDockerHost().getPort(); - - if (port == -1) { - throw new RuntimeException("no port configured for " + host); - } - - DuplexChannel channel = (DuplexChannel) bootstrap.connect(host, port).sync().channel(); - - if (dockerClientConfig.getDockerTlsVerify()) { - final SslHandler ssl = initSsl(dockerClientConfig); - - if (ssl != null) { - channel.pipeline().addFirst(ssl); - } - } - - return channel; - } - - private SslHandler initSsl(DockerClientConfig dockerClientConfig) { - SslHandler ssl = null; - - try { - String host = dockerClientConfig.getDockerHost().getHost(); - int port = dockerClientConfig.getDockerHost().getPort(); - - if (sslContext == null) { - sslContext = new LocalDirectorySSLConfig(dockerClientConfig.getDockerCertPath()).getSSLContext(); - } - - SSLEngine engine = sslContext.createSSLEngine(host, port); - engine.setUseClientMode(true); - engine.setSSLParameters(enableHostNameVerification(engine.getSSLParameters())); - - // in the future we may use HostnameVerifier like here: - // https://github.com/AsyncHttpClient/async-http-client/blob/1.8.x/src/main/java/com/ning/http/client/providers/netty/NettyConnectListener.java#L76 - - ssl = new SslHandler(engine); - - } catch (Exception e) { - throw new RuntimeException(e); - } - - return ssl; - } - } - - protected DockerClientConfig getDockerClientConfig() { - checkNotNull(dockerClientConfig, - "Factor not initialized, dockerClientConfig not set. You probably forgot to call init()!"); - return dockerClientConfig; - } - - public SSLParameters enableHostNameVerification(SSLParameters sslParameters) { - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); - return sslParameters; - } - - @Override - public CopyArchiveFromContainerCmd.Exec createCopyArchiveFromContainerCmdExec() { - return new CopyArchiveFromContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CopyArchiveToContainerCmd.Exec createCopyArchiveToContainerCmdExec() { - return new CopyArchiveToContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public AuthCmd.Exec createAuthCmdExec() { - return new AuthCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InfoCmd.Exec createInfoCmdExec() { - return new InfoCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public PingCmd.Exec createPingCmdExec() { - return new PingCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public VersionCmd.Exec createVersionCmdExec() { - return new VersionCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public PullImageCmd.Exec createPullImageCmdExec() { - return new PullImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public PushImageCmd.Exec createPushImageCmdExec() { - return new PushImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public SaveImageCmd.Exec createSaveImageCmdExec() { - return new SaveImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CreateImageCmd.Exec createCreateImageCmdExec() { - return new CreateImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public SearchImagesCmd.Exec createSearchImagesCmdExec() { - return new SearchImagesCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RemoveImageCmd.Exec createRemoveImageCmdExec() { - return new RemoveImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ListImagesCmd.Exec createListImagesCmdExec() { - return new ListImagesCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InspectImageCmd.Exec createInspectImageCmdExec() { - return new InspectImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ListContainersCmd.Exec createListContainersCmdExec() { - return new ListContainersCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CreateContainerCmd.Exec createCreateContainerCmdExec() { - return new CreateContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public StartContainerCmd.Exec createStartContainerCmdExec() { - return new StartContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InspectContainerCmd.Exec createInspectContainerCmdExec() { - return new InspectContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ExecCreateCmd.Exec createExecCmdExec() { - return new ExecCreateCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RemoveContainerCmd.Exec createRemoveContainerCmdExec() { - return new RemoveContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public WaitContainerCmd.Exec createWaitContainerCmdExec() { - return new WaitContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public AttachContainerCmd.Exec createAttachContainerCmdExec() { - return new AttachContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ExecStartCmd.Exec createExecStartCmdExec() { - return new ExecStartCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InspectExecCmd.Exec createInspectExecCmdExec() { - return new InspectExecCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public LogContainerCmd.Exec createLogContainerCmdExec() { - return new LogContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec() { - return new CopyFileFromContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public StopContainerCmd.Exec createStopContainerCmdExec() { - return new StopContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ContainerDiffCmd.Exec createContainerDiffCmdExec() { - return new ContainerDiffCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public KillContainerCmd.Exec createKillContainerCmdExec() { - return new KillContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public UpdateContainerCmd.Exec createUpdateContainerCmdExec() { - return new UpdateContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RenameContainerCmd.Exec createRenameContainerCmdExec() { - return new RenameContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RestartContainerCmd.Exec createRestartContainerCmdExec() { - return new RestartContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CommitCmd.Exec createCommitCmdExec() { - return new CommitCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public BuildImageCmd.Exec createBuildImageCmdExec() { - return new BuildImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public TopContainerCmd.Exec createTopContainerCmdExec() { - return new TopContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public TagImageCmd.Exec createTagImageCmdExec() { - return new TagImageCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public PauseContainerCmd.Exec createPauseContainerCmdExec() { - return new PauseContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec() { - return new UnpauseContainerCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public EventsCmd.Exec createEventsCmdExec() { - return new EventsCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public StatsCmd.Exec createStatsCmdExec() { - return new StatsCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CreateVolumeCmd.Exec createCreateVolumeCmdExec() { - return new CreateVolumeCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InspectVolumeCmd.Exec createInspectVolumeCmdExec() { - return new InspectVolumeCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RemoveVolumeCmd.Exec createRemoveVolumeCmdExec() { - return new RemoveVolumeCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ListVolumesCmd.Exec createListVolumesCmdExec() { - return new ListVolumesCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ListNetworksCmd.Exec createListNetworksCmdExec() { - return new ListNetworksCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public InspectNetworkCmd.Exec createInspectNetworkCmdExec() { - return new InspectNetworkCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public CreateNetworkCmd.Exec createCreateNetworkCmdExec() { - return new CreateNetworkCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public RemoveNetworkCmd.Exec createRemoveNetworkCmdExec() { - return new RemoveNetworkCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public ConnectToNetworkCmd.Exec createConnectToNetworkCmdExec() { - return new ConnectToNetworkCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public DisconnectFromNetworkCmd.Exec createDisconnectFromNetworkCmdExec() { - return new DisconnectFromNetworkCmdExec(getBaseResource(), getDockerClientConfig()); - } - - @Override - public void close() throws IOException { - checkNotNull(eventLoopGroup, "Factory not initialized. You probably forgot to call init()!"); - - eventLoopGroup.shutdownGracefully(); - } - - @Override - public DockerCmdExecFactory withSSLContext(SSLContext sslContext) { - this.sslContext = sslContext; - return this; - } - - private WebTarget getBaseResource() { - return new WebTarget(channelProvider); - } +@Deprecated +public class DockerCmdExecFactoryImpl extends NettyDockerCmdExecFactory { } diff --git a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java index 9669d6818..26b950e4e 100644 --- a/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java +++ b/src/main/java/com/github/dockerjava/netty/InvocationBuilder.java @@ -27,6 +27,7 @@ import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CountDownLatch; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -69,6 +70,63 @@ public void onNext(T object) { } } + public class SkipResultCallback extends ResultCallbackTemplate, Void> { + @Override + public void onNext(Void object) { + } + } + + /** + * Implementation of {@link ResultCallback} with the single result event expected. + */ + public static class AsyncResultCallback + extends ResultCallbackTemplate, A_RES_T> { + + private A_RES_T result = null; + + private final CountDownLatch resultReady = new CountDownLatch(1); + + @Override + public void onNext(A_RES_T object) { + onResult(object); + } + + private void onResult(A_RES_T object) { + if (resultReady.getCount() == 0) { + throw new IllegalStateException("Result has already been set"); + } + + try { + result = object; + } finally { + resultReady.countDown(); + } + } + + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + resultReady.countDown(); + } + } + + /** + * Blocks until {@link ResultCallback#onNext(Object)} was called for the first time + */ + @SuppressWarnings("unchecked") + public A_RES_T awaitResult() { + try { + resultReady.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + getFirstError(); + return result; + } + } + private ChannelProvider channelProvider; private String resource; @@ -197,7 +255,7 @@ public InputStream post(final Object entity) { Channel channel = getChannel(); - ResponseCallback callback = new ResponseCallback(); + AsyncResultCallback callback = new AsyncResultCallback<>(); HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, callback); HttpResponseStreamHandler streamHandler = new HttpResponseStreamHandler(callback); @@ -401,6 +459,31 @@ public void post(TypeReference typeReference, ResultCallback resultCal channel.pipeline().addLast(new JsonObjectDecoder()); channel.pipeline().addLast(jsonResponseHandler); + postChunkedStreamRequest(requestProvider, channel, body); + } + + public void postStream(InputStream body) { + SkipResultCallback resultCallback = new SkipResultCallback(); + + HttpRequestProvider requestProvider = httpPostRequestProvider(null); + + Channel channel = getChannel(); + + HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, resultCallback); + + channel.pipeline().addLast(new ChunkedWriteHandler()); + channel.pipeline().addLast(responseHandler); + + postChunkedStreamRequest(requestProvider, channel, body); + + try { + resultCallback.awaitCompletion(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private void postChunkedStreamRequest(HttpRequestProvider requestProvider, Channel channel, InputStream body) { HttpRequest request = requestProvider.getHttpRequest(resource); // don't accept FullHttpRequest here @@ -423,7 +506,7 @@ public InputStream get() { Channel channel = getChannel(); - ResponseCallback resultCallback = new ResponseCallback(); + AsyncResultCallback resultCallback = new AsyncResultCallback<>(); HttpResponseHandler responseHandler = new HttpResponseHandler(requestProvider, resultCallback); diff --git a/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java b/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java new file mode 100644 index 000000000..c91d4af21 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java @@ -0,0 +1,624 @@ +package com.github.dockerjava.netty; + +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.ConnectToNetworkCmd; +import com.github.dockerjava.api.command.ContainerDiffCmd; +import com.github.dockerjava.api.command.CopyArchiveFromContainerCmd; +import com.github.dockerjava.api.command.CopyArchiveToContainerCmd; +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.CreateNetworkCmd; +import com.github.dockerjava.api.command.CreateVolumeCmd; +import com.github.dockerjava.api.command.DisconnectFromNetworkCmd; +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.InspectNetworkCmd; +import com.github.dockerjava.api.command.InspectVolumeCmd; +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.ListNetworksCmd; +import com.github.dockerjava.api.command.ListVolumesCmd; +import com.github.dockerjava.api.command.LoadImageCmd; +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.RemoveNetworkCmd; +import com.github.dockerjava.api.command.RemoveVolumeCmd; +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; +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.UpdateContainerCmd; +import com.github.dockerjava.api.command.VersionCmd; +import com.github.dockerjava.api.command.WaitContainerCmd; +import com.github.dockerjava.api.command.RenameContainerCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DockerClientImpl; +import com.github.dockerjava.core.SSLConfig; +import com.github.dockerjava.netty.exec.AttachContainerCmdExec; +import com.github.dockerjava.netty.exec.AuthCmdExec; +import com.github.dockerjava.netty.exec.BuildImageCmdExec; +import com.github.dockerjava.netty.exec.CommitCmdExec; +import com.github.dockerjava.netty.exec.ConnectToNetworkCmdExec; +import com.github.dockerjava.netty.exec.ContainerDiffCmdExec; +import com.github.dockerjava.netty.exec.CopyArchiveFromContainerCmdExec; +import com.github.dockerjava.netty.exec.CopyArchiveToContainerCmdExec; +import com.github.dockerjava.netty.exec.CopyFileFromContainerCmdExec; +import com.github.dockerjava.netty.exec.CreateContainerCmdExec; +import com.github.dockerjava.netty.exec.CreateImageCmdExec; +import com.github.dockerjava.netty.exec.CreateNetworkCmdExec; +import com.github.dockerjava.netty.exec.CreateVolumeCmdExec; +import com.github.dockerjava.netty.exec.DisconnectFromNetworkCmdExec; +import com.github.dockerjava.netty.exec.EventsCmdExec; +import com.github.dockerjava.netty.exec.ExecCreateCmdExec; +import com.github.dockerjava.netty.exec.ExecStartCmdExec; +import com.github.dockerjava.netty.exec.InfoCmdExec; +import com.github.dockerjava.netty.exec.InspectContainerCmdExec; +import com.github.dockerjava.netty.exec.InspectExecCmdExec; +import com.github.dockerjava.netty.exec.InspectImageCmdExec; +import com.github.dockerjava.netty.exec.InspectNetworkCmdExec; +import com.github.dockerjava.netty.exec.InspectVolumeCmdExec; +import com.github.dockerjava.netty.exec.KillContainerCmdExec; +import com.github.dockerjava.netty.exec.ListContainersCmdExec; +import com.github.dockerjava.netty.exec.ListImagesCmdExec; +import com.github.dockerjava.netty.exec.ListNetworksCmdExec; +import com.github.dockerjava.netty.exec.ListVolumesCmdExec; +import com.github.dockerjava.netty.exec.LoadImageCmdExec; +import com.github.dockerjava.netty.exec.LogContainerCmdExec; +import com.github.dockerjava.netty.exec.PauseContainerCmdExec; +import com.github.dockerjava.netty.exec.PingCmdExec; +import com.github.dockerjava.netty.exec.PullImageCmdExec; +import com.github.dockerjava.netty.exec.PushImageCmdExec; +import com.github.dockerjava.netty.exec.RemoveContainerCmdExec; +import com.github.dockerjava.netty.exec.RemoveImageCmdExec; +import com.github.dockerjava.netty.exec.RemoveNetworkCmdExec; +import com.github.dockerjava.netty.exec.RemoveVolumeCmdExec; +import com.github.dockerjava.netty.exec.RestartContainerCmdExec; +import com.github.dockerjava.netty.exec.SaveImageCmdExec; +import com.github.dockerjava.netty.exec.SearchImagesCmdExec; +import com.github.dockerjava.netty.exec.StartContainerCmdExec; +import com.github.dockerjava.netty.exec.StatsCmdExec; +import com.github.dockerjava.netty.exec.StopContainerCmdExec; +import com.github.dockerjava.netty.exec.TagImageCmdExec; +import com.github.dockerjava.netty.exec.TopContainerCmdExec; +import com.github.dockerjava.netty.exec.UnpauseContainerCmdExec; +import com.github.dockerjava.netty.exec.UpdateContainerCmdExec; +import com.github.dockerjava.netty.exec.VersionCmdExec; +import com.github.dockerjava.netty.exec.WaitContainerCmdExec; +import com.github.dockerjava.netty.exec.RenameContainerCmdExec; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelConfig; +import io.netty.channel.ChannelFactory; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.EpollDomainSocketChannel; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.DuplexChannel; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.channel.unix.DomainSocketAddress; +import io.netty.channel.unix.UnixChannel; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.ssl.SslHandler; +import io.netty.util.concurrent.DefaultThreadFactory; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.security.Security; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Experimental implementation of {@link DockerCmdExecFactory} that supports http connection hijacking that is needed to pass STDIN to the + * container. + * + * To use it just pass an instance via {@link DockerClientImpl#withDockerCmdExecFactory(DockerCmdExecFactory)} + * + * @see https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/#attach-to-a-container + * @see https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/#exec-start + * + * + * @author Marcus Linke + */ +public class NettyDockerCmdExecFactory implements DockerCmdExecFactory { + + private static String threadPrefix = "dockerjava-netty"; + + /* + * useful links: + * + * http://stackoverflow.com/questions/33296749/netty-connect-to-unix-domain-socket-failed + * http://netty.io/wiki/native-transports.html + * https://github.com/netty/netty/blob/master/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java + * https://github.com/slandelle/netty-request-chunking/blob/master/src/test/java/slandelle/ChunkingTest.java + */ + + private DockerClientConfig dockerClientConfig; + + private Bootstrap bootstrap; + + private EventLoopGroup eventLoopGroup; + + private NettyInitializer nettyInitializer; + + private ChannelProvider channelProvider = new ChannelProvider() { + @Override + public DuplexChannel getChannel() { + DuplexChannel channel = connect(); + channel.pipeline().addLast(new LoggingHandler(getClass())); + return channel; + } + }; + + private Integer connectTimeout = null; + + @Override + public void init(DockerClientConfig dockerClientConfig) { + checkNotNull(dockerClientConfig, "config was not specified"); + this.dockerClientConfig = dockerClientConfig; + + bootstrap = new Bootstrap(); + + String scheme = dockerClientConfig.getDockerHost().getScheme(); + + if ("unix".equals(scheme)) { + nettyInitializer = new UnixDomainSocketInitializer(); + } else if ("tcp".equals(scheme)) { + nettyInitializer = new InetSocketInitializer(); + } + + eventLoopGroup = nettyInitializer.init(bootstrap, dockerClientConfig); + } + + private DuplexChannel connect() { + try { + return connect(bootstrap); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private DuplexChannel connect(final Bootstrap bootstrap) throws InterruptedException { + return nettyInitializer.connect(bootstrap); + } + + private interface NettyInitializer { + EventLoopGroup init(final Bootstrap bootstrap, DockerClientConfig dockerClientConfig); + + DuplexChannel connect(final Bootstrap bootstrap) throws InterruptedException; + } + + private class UnixDomainSocketInitializer implements NettyInitializer { + @Override + public EventLoopGroup init(Bootstrap bootstrap, DockerClientConfig dockerClientConfig) { + EventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(0, new DefaultThreadFactory(threadPrefix)); + + ChannelFactory factory = new ChannelFactory() { + @Override + public EpollDomainSocketChannel newChannel() { + return configure(new EpollDomainSocketChannel()); + } + }; + + bootstrap.group(epollEventLoopGroup).channelFactory(factory) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(final UnixChannel channel) throws Exception { + channel.pipeline().addLast(new HttpClientCodec()); + } + }); + return epollEventLoopGroup; + } + + @Override + public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { + return (DuplexChannel) bootstrap.connect(new DomainSocketAddress("/var/run/docker.sock")).sync().channel(); + } + } + + private class InetSocketInitializer implements NettyInitializer { + @Override + public EventLoopGroup init(Bootstrap bootstrap, final DockerClientConfig dockerClientConfig) { + EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(0, new DefaultThreadFactory(threadPrefix)); + + InetAddress addr = InetAddress.getLoopbackAddress(); + + final SocketAddress proxyAddress = new InetSocketAddress(addr, 8008); + + Security.addProvider(new BouncyCastleProvider()); + + ChannelFactory factory = new ChannelFactory() { + @Override + public NioSocketChannel newChannel() { + return configure(new NioSocketChannel()); + } + }; + + bootstrap.group(nioEventLoopGroup).channelFactory(factory) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(final SocketChannel channel) throws Exception { + // channel.pipeline().addLast(new + // HttpProxyHandler(proxyAddress)); + channel.pipeline().addLast(new HttpClientCodec()); + } + }); + + return nioEventLoopGroup; + } + + @Override + public DuplexChannel connect(Bootstrap bootstrap) throws InterruptedException { + String host = dockerClientConfig.getDockerHost().getHost(); + int port = dockerClientConfig.getDockerHost().getPort(); + + if (port == -1) { + throw new RuntimeException("no port configured for " + host); + } + + DuplexChannel channel = (DuplexChannel) bootstrap.connect(host, port).sync().channel(); + + final SslHandler ssl = initSsl(dockerClientConfig); + + if (ssl != null) { + channel.pipeline().addFirst(ssl); + } + + return channel; + } + + private SslHandler initSsl(DockerClientConfig dockerClientConfig) { + SslHandler ssl = null; + + try { + String host = dockerClientConfig.getDockerHost().getHost(); + int port = dockerClientConfig.getDockerHost().getPort(); + + final SSLConfig sslConfig = dockerClientConfig.getSSLConfig(); + + if (sslConfig != null && sslConfig.getSSLContext() != null) { + + SSLEngine engine = sslConfig.getSSLContext().createSSLEngine(host, port); + engine.setUseClientMode(true); + engine.setSSLParameters(enableHostNameVerification(engine.getSSLParameters())); + + // in the future we may use HostnameVerifier like here: + // https://github.com/AsyncHttpClient/async-http-client/blob/1.8.x/src/main/java/com/ning/http/client/providers/netty/NettyConnectListener.java#L76 + + ssl = new SslHandler(engine); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + + return ssl; + } + } + + protected DockerClientConfig getDockerClientConfig() { + checkNotNull(dockerClientConfig, + "Factor not initialized, dockerClientConfig not set. You probably forgot to call init()!"); + return dockerClientConfig; + } + + public SSLParameters enableHostNameVerification(SSLParameters sslParameters) { + sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); + return sslParameters; + } + + @Override + public CopyArchiveFromContainerCmd.Exec createCopyArchiveFromContainerCmdExec() { + return new CopyArchiveFromContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CopyArchiveToContainerCmd.Exec createCopyArchiveToContainerCmdExec() { + return new CopyArchiveToContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public AuthCmd.Exec createAuthCmdExec() { + return new AuthCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InfoCmd.Exec createInfoCmdExec() { + return new InfoCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public PingCmd.Exec createPingCmdExec() { + return new PingCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public VersionCmd.Exec createVersionCmdExec() { + return new VersionCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public PullImageCmd.Exec createPullImageCmdExec() { + return new PullImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public PushImageCmd.Exec createPushImageCmdExec() { + return new PushImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public SaveImageCmd.Exec createSaveImageCmdExec() { + return new SaveImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CreateImageCmd.Exec createCreateImageCmdExec() { + return new CreateImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public LoadImageCmd.Exec createLoadImageCmdExec() { + return new LoadImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public SearchImagesCmd.Exec createSearchImagesCmdExec() { + return new SearchImagesCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RemoveImageCmd.Exec createRemoveImageCmdExec() { + return new RemoveImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ListImagesCmd.Exec createListImagesCmdExec() { + return new ListImagesCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InspectImageCmd.Exec createInspectImageCmdExec() { + return new InspectImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ListContainersCmd.Exec createListContainersCmdExec() { + return new ListContainersCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CreateContainerCmd.Exec createCreateContainerCmdExec() { + return new CreateContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public StartContainerCmd.Exec createStartContainerCmdExec() { + return new StartContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InspectContainerCmd.Exec createInspectContainerCmdExec() { + return new InspectContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ExecCreateCmd.Exec createExecCmdExec() { + return new ExecCreateCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RemoveContainerCmd.Exec createRemoveContainerCmdExec() { + return new RemoveContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public WaitContainerCmd.Exec createWaitContainerCmdExec() { + return new WaitContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public AttachContainerCmd.Exec createAttachContainerCmdExec() { + return new AttachContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ExecStartCmd.Exec createExecStartCmdExec() { + return new ExecStartCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InspectExecCmd.Exec createInspectExecCmdExec() { + return new InspectExecCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public LogContainerCmd.Exec createLogContainerCmdExec() { + return new LogContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CopyFileFromContainerCmd.Exec createCopyFileFromContainerCmdExec() { + return new CopyFileFromContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public StopContainerCmd.Exec createStopContainerCmdExec() { + return new StopContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ContainerDiffCmd.Exec createContainerDiffCmdExec() { + return new ContainerDiffCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public KillContainerCmd.Exec createKillContainerCmdExec() { + return new KillContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public UpdateContainerCmd.Exec createUpdateContainerCmdExec() { + return new UpdateContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RenameContainerCmd.Exec createRenameContainerCmdExec() { + return new RenameContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RestartContainerCmd.Exec createRestartContainerCmdExec() { + return new RestartContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CommitCmd.Exec createCommitCmdExec() { + return new CommitCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public BuildImageCmd.Exec createBuildImageCmdExec() { + return new BuildImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public TopContainerCmd.Exec createTopContainerCmdExec() { + return new TopContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public TagImageCmd.Exec createTagImageCmdExec() { + return new TagImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public PauseContainerCmd.Exec createPauseContainerCmdExec() { + return new PauseContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public UnpauseContainerCmd.Exec createUnpauseContainerCmdExec() { + return new UnpauseContainerCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public EventsCmd.Exec createEventsCmdExec() { + return new EventsCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public StatsCmd.Exec createStatsCmdExec() { + return new StatsCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CreateVolumeCmd.Exec createCreateVolumeCmdExec() { + return new CreateVolumeCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InspectVolumeCmd.Exec createInspectVolumeCmdExec() { + return new InspectVolumeCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RemoveVolumeCmd.Exec createRemoveVolumeCmdExec() { + return new RemoveVolumeCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ListVolumesCmd.Exec createListVolumesCmdExec() { + return new ListVolumesCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ListNetworksCmd.Exec createListNetworksCmdExec() { + return new ListNetworksCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public InspectNetworkCmd.Exec createInspectNetworkCmdExec() { + return new InspectNetworkCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public CreateNetworkCmd.Exec createCreateNetworkCmdExec() { + return new CreateNetworkCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public RemoveNetworkCmd.Exec createRemoveNetworkCmdExec() { + return new RemoveNetworkCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public ConnectToNetworkCmd.Exec createConnectToNetworkCmdExec() { + return new ConnectToNetworkCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public DisconnectFromNetworkCmd.Exec createDisconnectFromNetworkCmdExec() { + return new DisconnectFromNetworkCmdExec(getBaseResource(), getDockerClientConfig()); + } + + @Override + public void close() throws IOException { + checkNotNull(eventLoopGroup, "Factory not initialized. You probably forgot to call init()!"); + + eventLoopGroup.shutdownGracefully(); + } + + /** + * Configure connection timeout in milliseconds + */ + public NettyDockerCmdExecFactory withConnectTimeout(Integer connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + private T configure(T channel) { + ChannelConfig channelConfig = channel.config(); + + if (connectTimeout != null) { + channelConfig.setConnectTimeoutMillis(connectTimeout); + } + + return channel; + } + + private WebTarget getBaseResource() { + return new WebTarget(channelProvider); + } +} diff --git a/src/main/java/com/github/dockerjava/netty/WebTarget.java b/src/main/java/com/github/dockerjava/netty/WebTarget.java index 090fea67c..ef1510f5c 100644 --- a/src/main/java/com/github/dockerjava/netty/WebTarget.java +++ b/src/main/java/com/github/dockerjava/netty/WebTarget.java @@ -2,12 +2,14 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + /** * This class is basically a replacement of javax.ws.rs.client.WebTarget to allow simpler migration of JAX-RS code to a netty based * implementation. @@ -16,32 +18,40 @@ */ public class WebTarget { - private ChannelProvider channelProvider; + private final ChannelProvider channelProvider; - private List path = new ArrayList(); + private final ImmutableList path; - private Map queryParams = new HashMap(); + private final ImmutableMap queryParams; private static final String PATH_SEPARATOR = "/"; public WebTarget(ChannelProvider channelProvider) { + this(channelProvider, ImmutableList.of(), ImmutableMap.of()); + } + + private WebTarget(ChannelProvider channelProvider, + ImmutableList path, + ImmutableMap queryParams) { this.channelProvider = channelProvider; + this.path = path; + this.queryParams = queryParams; } public WebTarget path(String... components) { + ImmutableList.Builder newPath = ImmutableList.builder().addAll(this.path); for (String component : components) { - - path.addAll(Arrays.asList(StringUtils.split(component, PATH_SEPARATOR))); + newPath.addAll(Arrays.asList(StringUtils.split(component, PATH_SEPARATOR))); } - return this; + return new WebTarget(channelProvider, newPath.build(), queryParams); } public InvocationBuilder request() { String resource = PATH_SEPARATOR + StringUtils.join(path, PATH_SEPARATOR); - List params = new ArrayList(); + List params = new ArrayList<>(); for (Map.Entry entry : queryParams.entrySet()) { params.add(entry.getKey() + "=" + entry.getValue()); } @@ -54,20 +64,47 @@ public InvocationBuilder request() { } public WebTarget resolveTemplate(String name, Object value) { - List newPath = new ArrayList(); + ImmutableList.Builder newPath = ImmutableList.builder(); for (String component : path) { component = component.replaceAll("\\{" + name + "\\}", value.toString()); newPath.add(component); } - path = newPath; - return this; + return new WebTarget(channelProvider, newPath.build(), queryParams); } public WebTarget queryParam(String name, Object value) { + ImmutableMap.Builder builder = ImmutableMap.builder().putAll(queryParams); if (value != null) { - queryParams.put(name, value.toString()); + builder.put(name, value.toString()); } - return this; + return new WebTarget(channelProvider, path, builder.build()); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + WebTarget webTarget = (WebTarget) o; + + if (channelProvider != null ? !channelProvider.equals(webTarget.channelProvider) : webTarget.channelProvider != null) { + return false; + } + if (path != null ? !path.equals(webTarget.path) : webTarget.path != null) { + return false; + } + return queryParams != null ? queryParams.equals(webTarget.queryParams) : webTarget.queryParams == null; + } + + @Override + public int hashCode() { + int result = channelProvider != null ? channelProvider.hashCode() : 0; + result = 31 * result + (path != null ? path.hashCode() : 0); + result = 31 * result + (queryParams != null ? queryParams.hashCode() : 0); + return result; + } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java index f4ff04576..43152570e 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/BuildImageCmdExec.java @@ -15,6 +15,7 @@ import com.github.dockerjava.netty.WebTarget; import java.io.IOException; +import java.util.Map; public class BuildImageCmdExec extends AbstrAsyncDockerCmdExec implements BuildImageCmd.Exec { @@ -85,18 +86,14 @@ protected Void execute0(BuildImageCmd command, ResultCallback webTarget = webTarget.queryParam("cpusetcpus", command.getCpusetcpus()); } - if (command.getBuildArgs() != null && !command.getBuildArgs().isEmpty()) { - try { - webTarget = webTarget.queryParam("buildargs", MAPPER.writeValueAsString(command.getBuildArgs())); - } catch (IOException e) { - throw new RuntimeException(e); - } - } + webTarget = writeMap(webTarget, "buildargs", command.getBuildArgs()); if (command.getShmsize() != null) { webTarget = webTarget.queryParam("shmsize", command.getShmsize()); } + webTarget = writeMap(webTarget, "labels", command.getLabels()); + LOGGER.trace("POST: {}", webTarget); InvocationBuilder builder = resourceWithOptionalAuthConfig(command, webTarget.request()) @@ -109,4 +106,16 @@ protected Void execute0(BuildImageCmd command, ResultCallback return null; } + + private WebTarget writeMap(WebTarget webTarget, String name, Map value) { + if (value != null && !value.isEmpty()) { + try { + return webTarget.queryParam(name, MAPPER.writeValueAsString(value)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + return webTarget; + } + } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/LoadImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/LoadImageCmdExec.java new file mode 100644 index 000000000..30ee716a5 --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/LoadImageCmdExec.java @@ -0,0 +1,28 @@ +package com.github.dockerjava.netty.exec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.dockerjava.api.command.LoadImageCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; + +public class LoadImageCmdExec extends AbstrSyncDockerCmdExec implements + LoadImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(LoadImageCmdExec.class); + + public LoadImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(LoadImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/load"); + + LOGGER.trace("POST: {}", webResource); + webResource.request().postStream(command.getImageStream()); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java index 12f2d8cbc..039006f2d 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/StartContainerCmdExec.java @@ -23,7 +23,9 @@ protected Void execute(StartContainerCmd command) { command.getContainerId()); LOGGER.trace("POST: {}", webResource); - webResource.request().accept(MediaType.APPLICATION_JSON).post(command); + webResource.request() + .accept(MediaType.APPLICATION_JSON) + .post(command); return null; } diff --git a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java index 706228d04..596334640 100644 --- a/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java +++ b/src/main/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandler.java @@ -6,9 +6,6 @@ import java.io.IOException; import java.io.InputStream; -import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import com.github.dockerjava.api.async.ResultCallback; @@ -19,45 +16,87 @@ */ public class HttpResponseStreamHandler extends SimpleChannelInboundHandler { - private HttpResponseInputStream stream = new HttpResponseInputStream(); + private ResultCallback resultCallback; + + private final HttpResponseInputStream stream = new HttpResponseInputStream(); public HttpResponseStreamHandler(ResultCallback resultCallback) { - resultCallback.onNext(stream); + this.resultCallback = resultCallback; } @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + invokeCallbackOnFirstRead(); + stream.write(msg.copy()); } + private void invokeCallbackOnFirstRead() { + if (resultCallback != null) { + resultCallback.onNext(stream); + resultCallback = null; + } + } + @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - stream.close(); - super.channelReadComplete(ctx); + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + stream.writeComplete(); + + super.channelInactive(ctx); } public static class HttpResponseInputStream extends InputStream { - private AtomicBoolean closed = new AtomicBoolean(false); + private boolean writeCompleted = false; - private LinkedTransferQueue queue = new LinkedTransferQueue(); + private boolean closed = false; private ByteBuf current = null; - public void write(ByteBuf byteBuf) { - queue.put(byteBuf); + private final Object lock = new Object(); + + public void write(ByteBuf byteBuf) throws InterruptedException { + synchronized (lock) { + if (closed) { + return; + } + while (current != null) { + lock.wait(); + + if (closed) { + return; + } + } + current = byteBuf; + + lock.notifyAll(); + } + } + + public void writeComplete() { + synchronized (lock) { + writeCompleted = true; + + lock.notifyAll(); + } } @Override public void close() throws IOException { - closed.set(true); - super.close(); + synchronized (lock) { + closed = true; + releaseCurrent(); + + lock.notifyAll(); + } } @Override public int available() throws IOException { - poll(); - return readableBytes(); + synchronized (lock) { + poll(0); + return readableBytes(); + } } private int readableBytes() { @@ -66,34 +105,72 @@ private int readableBytes() { } else { return 0; } - } @Override public int read() throws IOException { + byte[] b = new byte[1]; + int n = read(b, 0, 1); + return n != -1 ? b[0] : -1; + } - poll(); + @Override + public int read(byte[] b, int off, int len) throws IOException { + synchronized (lock) { + off = poll(off); - if (readableBytes() == 0) { - if (closed.get()) { + if (current == null) { return -1; + } else { + int availableBytes = Math.min(len, current.readableBytes() - off); + current.readBytes(b, off, availableBytes); + return availableBytes; } } + } - if (current != null && current.readableBytes() > 0) { - return current.readByte() & 0xff; - } else { - return read(); + private int poll(int off) throws IOException { + synchronized (lock) { + while (readableBytes() <= off) { + try { + if (closed) { + throw new IOException("Stream closed"); + } + + off -= releaseCurrent(); + if (writeCompleted) { + return off; + } + while (current == null) { + lock.wait(); + + if (closed) { + throw new IOException("Stream closed"); + } + if (writeCompleted && current == null) { + return off; + } + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + return off; } } - private void poll() { - if (readableBytes() == 0) { - try { - current = queue.poll(50, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - throw new RuntimeException(e); + private int releaseCurrent() { + synchronized (lock) { + if (current != null) { + int n = current.readableBytes(); + current.release(); + current = null; + + lock.notifyAll(); + + return n; } + return 0; } } } diff --git a/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java b/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java new file mode 100644 index 000000000..17c339e3e --- /dev/null +++ b/src/main/java/org/apache/http/impl/io/ChunkedInputStream.java @@ -0,0 +1,337 @@ +// Modified version (see https://github.com/docker-java/docker-java/pull/697) +/* + * ==================================================================== + * 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.impl.io; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.http.ConnectionClosedException; +import org.apache.http.Header; +import org.apache.http.HttpException; +import org.apache.http.MalformedChunkCodingException; +import org.apache.http.TruncatedChunkException; +import org.apache.http.config.MessageConstraints; +import org.apache.http.io.BufferInfo; +import org.apache.http.io.SessionInputBuffer; +import org.apache.http.util.Args; +import org.apache.http.util.CharArrayBuffer; + +/** + * Implements chunked transfer coding. The content is received in small chunks. + * Entities transferred using this input stream can be of unlimited length. + * After the stream is read to the end, it provides access to the trailers, + * if any. + *

+ * Note that this class NEVER closes the underlying stream, even when close + * gets called. Instead, it will read until the "end" of its chunking on + * close, which allows for the seamless execution of subsequent HTTP 1.1 + * requests, while not requiring the client to remember to read the entire + * contents of the response. + * + * + * @since 4.0 + * + */ +public class ChunkedInputStream extends InputStream { + + private static final int CHUNK_LEN = 1; + private static final int CHUNK_DATA = 2; + private static final int CHUNK_CRLF = 3; + private static final int CHUNK_INVALID = Integer.MAX_VALUE; + + private static final int BUFFER_SIZE = 2048; + + /** The session input buffer */ + private final SessionInputBuffer in; + private final CharArrayBuffer buffer; + private final MessageConstraints constraints; + + private int state; + + /** The chunk size */ + private long chunkSize; + + /** The current position within the current chunk */ + private long pos; + + /** True if we've reached the end of stream */ + private boolean eof = false; + + /** True if this stream is closed */ + private boolean closed = false; + + private Header[] footers = new Header[] {}; + + /** + * Wraps session input stream and reads chunk coded input. + * + * @param in The session input buffer + * @param constraints Message constraints. If {@code null} + * {@link MessageConstraints#DEFAULT} will be used. + * + * @since 4.4 + */ + public ChunkedInputStream(final SessionInputBuffer in, final MessageConstraints constraints) { + super(); + this.in = Args.notNull(in, "Session input buffer"); + this.pos = 0L; + this.buffer = new CharArrayBuffer(16); + this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT; + this.state = CHUNK_LEN; + } + + /** + * Wraps session input stream and reads chunk coded input. + * + * @param in The session input buffer + */ + public ChunkedInputStream(final SessionInputBuffer in) { + this(in, null); + } + + @Override + public int available() throws IOException { + if (this.in instanceof BufferInfo) { + final int len = ((BufferInfo) this.in).length(); + return (int) Math.min(len, this.chunkSize - this.pos); + } else { + return 0; + } + } + + /** + *

Returns all the data in a chunked stream in coalesced form. A chunk + * is followed by a CRLF. The method returns -1 as soon as a chunksize of 0 + * is detected.

+ * + *

Trailer headers are read automatically at the end of the stream and + * can be obtained with the getResponseFooters() method.

+ * + * @return -1 of the end of the stream has been reached or the next data + * byte + * @throws IOException in case of an I/O error + */ + @Override + public int read() throws IOException { + if (this.closed) { + throw new IOException("Attempted read from closed stream."); + } + if (this.eof) { + return -1; + } + if (state != CHUNK_DATA) { + nextChunk(); + if (this.eof) { + return -1; + } + } + final int b = in.read(); + if (b != -1) { + pos++; + if (pos >= chunkSize) { + state = CHUNK_CRLF; + } + } + return b; + } + + /** + * Read some bytes from the stream. + * @param b The byte array that will hold the contents from the stream. + * @param off The offset into the byte array at which bytes will start to be + * placed. + * @param len the maximum number of bytes that can be returned. + * @return The number of bytes returned or -1 if the end of stream has been + * reached. + * @throws IOException in case of an I/O error + */ + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + + if (closed) { + throw new IOException("Attempted read from closed stream."); + } + + if (eof) { + return -1; + } + if (state != CHUNK_DATA) { + nextChunk(); + if (eof) { + return -1; + } + } + final int bytesRead = in.read(b, off, (int) Math.min(len, chunkSize - pos)); + if (bytesRead != -1) { + pos += bytesRead; + if (pos >= chunkSize) { + state = CHUNK_CRLF; + } + return bytesRead; + } else { + eof = true; + throw new TruncatedChunkException("Truncated chunk " + + "( expected size: " + chunkSize + + "; actual size: " + pos + ")"); + } + } + + /** + * Read some bytes from the stream. + * @param b The byte array that will hold the contents from the stream. + * @return The number of bytes returned or -1 if the end of stream has been + * reached. + * @throws IOException in case of an I/O error + */ + @Override + public int read(final byte[] b) throws IOException { + return read(b, 0, b.length); + } + + /** + * Read the next chunk. + * @throws IOException in case of an I/O error + */ + private void nextChunk() throws IOException { + if (state == CHUNK_INVALID) { + throw new MalformedChunkCodingException("Corrupt data stream"); + } + try { + chunkSize = getChunkSize(); + if (chunkSize < 0L) { + throw new MalformedChunkCodingException("Negative chunk size"); + } + state = CHUNK_DATA; + pos = 0L; + if (chunkSize == 0L) { + eof = true; + parseTrailerHeaders(); + } + } catch (MalformedChunkCodingException ex) { + state = CHUNK_INVALID; + throw ex; + } + } + + /** + * Expects the stream to start with a chunksize in hex with optional + * comments after a semicolon. The line must end with a CRLF: "a3; some + * comment\r\n" Positions the stream at the start of the next line. + */ + private long getChunkSize() throws IOException { + final int st = this.state; + switch (st) { + case CHUNK_CRLF: + this.buffer.clear(); + final int bytesRead1 = this.in.readLine(this.buffer); + if (bytesRead1 == -1) { + throw new MalformedChunkCodingException( + "CRLF expected at end of chunk"); + } + if (!this.buffer.isEmpty()) { + throw new MalformedChunkCodingException( + "Unexpected content at the end of chunk"); + } + state = CHUNK_LEN; + //$FALL-THROUGH$ + case CHUNK_LEN: + this.buffer.clear(); + final int bytesRead2 = this.in.readLine(this.buffer); + if (bytesRead2 == -1) { + throw new ConnectionClosedException("Premature end of chunk coded message body: " + + "closing chunk expected"); + } + int separator = this.buffer.indexOf(';'); + if (separator < 0) { + separator = this.buffer.length(); + } + final String s = this.buffer.substringTrimmed(0, separator); + try { + return Long.parseLong(s, 16); + } catch (final NumberFormatException e) { + throw new MalformedChunkCodingException("Bad chunk header: " + s); + } + default: + throw new IllegalStateException("Inconsistent codec state"); + } + } + + /** + * Reads and stores the Trailer headers. + * @throws IOException in case of an I/O error + */ + private void parseTrailerHeaders() throws IOException { + try { + this.footers = AbstractMessageParser.parseHeaders(in, + constraints.getMaxHeaderCount(), + constraints.getMaxLineLength(), + null); + } catch (final HttpException ex) { + final IOException ioe = new MalformedChunkCodingException("Invalid footer: " + + ex.getMessage()); + ioe.initCause(ex); + throw ioe; + } + } + + /** + * Upon close, this reads the remainder of the chunked message, + * leaving the underlying socket at a position to start reading the + * next response without scanning. + * @throws IOException in case of an I/O error + */ + @Override + public void close() throws IOException { + if (!closed) { + try { + if (!eof && state != CHUNK_INVALID) { + // read and discard the remainder of the message + final byte[] buff = new byte[BUFFER_SIZE]; + try { + while (read(buff) >= 0) { + continue; + } + } catch (ConnectionClosedException e) { + // just ignore + } catch (TruncatedChunkException e) { + // just ignore + } + } + } finally { + eof = true; + closed = true; + } + } + } + + public Header[] getFooters() { + return this.footers.clone(); + } + +} diff --git a/src/main/java/org/newsclub/net/unix/AFUNIXSocketImpl.java b/src/main/java/org/newsclub/net/unix/AFUNIXSocketImpl.java new file mode 100644 index 000000000..869d987f2 --- /dev/null +++ b/src/main/java/org/newsclub/net/unix/AFUNIXSocketImpl.java @@ -0,0 +1,415 @@ +// Modified version (see https://github.com/docker-java/docker-java/pull/697) +/** + * junixsocket + * + * Copyright (c) 2009,2014 Christian Kohlschütter + * + * The author 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 org.newsclub.net.unix; + +import java.io.FileDescriptor; +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.net.SocketImpl; +import java.net.SocketOptions; + +/** + * The Java-part of the {@link AFUNIXSocket} implementation. + * + * @author Christian Kohlschütter + */ +class AFUNIXSocketImpl extends SocketImpl { + private static final int SHUT_RD = 0; + private static final int SHUT_WR = 1; + private static final int SHUT_RD_WR = 2; + + private String socketFile; + private boolean closed = false; + private boolean bound = false; + private boolean connected = false; + + private boolean closedInputStream = false; + private boolean closedOutputStream = false; + + private final AFUNIXInputStream in = new AFUNIXInputStream(); + private final AFUNIXOutputStream out = new AFUNIXOutputStream(); + + AFUNIXSocketImpl() { + super(); + this.fd = new FileDescriptor(); + } + + FileDescriptor getFD() { + return fd; + } + + @Override + protected void accept(SocketImpl socket) throws IOException { + final AFUNIXSocketImpl si = (AFUNIXSocketImpl) socket; + NativeUnixSocket.accept(socketFile, fd, si.fd); + si.socketFile = socketFile; + si.connected = true; + } + + @Override + protected int available() throws IOException { + return NativeUnixSocket.available(fd); + } + + protected void bind(SocketAddress addr) throws IOException { + bind(0, addr); + } + + protected void bind(int backlog, SocketAddress addr) throws IOException { + if (!(addr instanceof AFUNIXSocketAddress)) { + throw new SocketException("Cannot bind to this type of address: " + addr.getClass()); + } + final AFUNIXSocketAddress socketAddress = (AFUNIXSocketAddress) addr; + socketFile = socketAddress.getSocketFile(); + NativeUnixSocket.bind(socketFile, fd, backlog); + bound = true; + this.localport = socketAddress.getPort(); + } + + @Override + @SuppressWarnings("hiding") + protected void bind(InetAddress host, int port) throws IOException { + throw new SocketException("Cannot bind to this type of address: " + InetAddress.class); + } + + private void checkClose() throws IOException { + //if (closedInputStream && closedOutputStream) { + // close(); + //} + } + + @Override + protected synchronized void close() throws IOException { + if (closed) { + return; + } + closed = true; + if (fd.valid()) { + NativeUnixSocket.shutdown(fd, SHUT_RD_WR); + NativeUnixSocket.close(fd); + } + if (bound) { + NativeUnixSocket.unlink(socketFile); + } + connected = false; + } + + @Override + @SuppressWarnings("hiding") + protected void connect(String host, int port) throws IOException { + throw new SocketException("Cannot bind to this type of address: " + InetAddress.class); + } + + @Override + @SuppressWarnings("hiding") + protected void connect(InetAddress address, int port) throws IOException { + throw new SocketException("Cannot bind to this type of address: " + InetAddress.class); + } + + @Override + protected void connect(SocketAddress addr, int timeout) throws IOException { + if (!(addr instanceof AFUNIXSocketAddress)) { + throw new SocketException("Cannot bind to this type of address: " + addr.getClass()); + } + final AFUNIXSocketAddress socketAddress = (AFUNIXSocketAddress) addr; + socketFile = socketAddress.getSocketFile(); + NativeUnixSocket.connect(socketFile, fd); + this.address = socketAddress.getAddress(); + this.port = socketAddress.getPort(); + this.localport = 0; + this.connected = true; + } + + @Override + protected void create(boolean stream) throws IOException { + } + + @Override + protected InputStream getInputStream() throws IOException { + if (!connected && !bound) { + throw new IOException("Not connected/not bound"); + } + return in; + } + + @Override + protected OutputStream getOutputStream() throws IOException { + if (!connected && !bound) { + throw new IOException("Not connected/not bound"); + } + return out; + } + + @Override + protected void listen(int backlog) throws IOException { + NativeUnixSocket.listen(fd, backlog); + } + + @Override + protected void sendUrgentData(int data) throws IOException { + NativeUnixSocket.write(fd, new byte[] {(byte) (data & 0xFF)}, 0, 1); + } + + private final class AFUNIXInputStream extends InputStream { + private boolean streamClosed = false; + + @Override + public int read(byte[] buf, int off, int len) throws IOException { + if (streamClosed) { + throw new IOException("This InputStream has already been closed."); + } + if (len == 0) { + return 0; + } + if (closed) { + return -1; + } + int maxRead = buf.length - off; + if (len > maxRead) { + len = maxRead; + } + try { + return NativeUnixSocket.read(fd, buf, off, len); + } catch (final IOException e) { + throw (IOException) new IOException(e.getMessage() + " at " + + AFUNIXSocketImpl.this.toString()).initCause(e); + } + } + + @Override + public int read() throws IOException { + final byte[] buf1 = new byte[1]; + final int numRead = read(buf1, 0, 1); + if (numRead <= 0) { + return -1; + } else { + return buf1[0] & 0xFF; + } + } + + @Override + public void close() throws IOException { + if (streamClosed) { + return; + } + streamClosed = true; + if (fd.valid()) { + NativeUnixSocket.shutdown(fd, SHUT_RD); + } + + closedInputStream = true; + checkClose(); + } + + @Override + public int available() throws IOException { + final int av = NativeUnixSocket.available(fd); + return av; + } + } + + private final class AFUNIXOutputStream extends OutputStream { + private boolean streamClosed = false; + + @Override + public void write(int oneByte) throws IOException { + final byte[] buf1 = new byte[] {(byte) oneByte}; + write(buf1, 0, 1); + } + + @Override + public void write(byte[] buf, int off, int len) throws IOException { + if (streamClosed) { + throw new AFUNIXSocketException("This OutputStream has already been closed."); + } + if (len > buf.length - off) { + throw new IndexOutOfBoundsException(); + } + try { + while (len > 0 && !Thread.interrupted()) { + final int written = NativeUnixSocket.write(fd, buf, off, len); + if (written == -1) { + throw new IOException("Unspecific error while writing"); + } + len -= written; + off += written; + } + } catch (final IOException e) { + throw (IOException) new IOException(e.getMessage() + " at " + + AFUNIXSocketImpl.this.toString()).initCause(e); + } + } + + @Override + public void close() throws IOException { + if (streamClosed) { + return; + } + streamClosed = true; + if (fd.valid()) { + NativeUnixSocket.shutdown(fd, SHUT_WR); + } + closedOutputStream = true; + checkClose(); + } + } + + @Override + public String toString() { + return super.toString() + "[fd=" + fd + "; file=" + this.socketFile + "; connected=" + + connected + "; bound=" + bound + "]"; + } + + private static int expectInteger(Object value) throws SocketException { + try { + return (Integer) value; + } catch (final ClassCastException e) { + throw new AFUNIXSocketException("Unsupported value: " + value, e); + } catch (final NullPointerException e) { + throw new AFUNIXSocketException("Value must not be null", e); + } + } + + private static int expectBoolean(Object value) throws SocketException { + try { + return ((Boolean) value).booleanValue() ? 1 : 0; + } catch (final ClassCastException e) { + throw new AFUNIXSocketException("Unsupported value: " + value, e); + } catch (final NullPointerException e) { + throw new AFUNIXSocketException("Value must not be null", e); + } + } + + @Override + public Object getOption(int optID) throws SocketException { + try { + switch (optID) { + case SocketOptions.SO_KEEPALIVE: + case SocketOptions.TCP_NODELAY: + return NativeUnixSocket.getSocketOptionInt(fd, optID) != 0 ? true : false; + case SocketOptions.SO_LINGER: + case SocketOptions.SO_TIMEOUT: + case SocketOptions.SO_RCVBUF: + case SocketOptions.SO_SNDBUF: + return NativeUnixSocket.getSocketOptionInt(fd, optID); + default: + throw new AFUNIXSocketException("Unsupported option: " + optID); + } + } catch (final AFUNIXSocketException e) { + throw e; + } catch (final Exception e) { + throw new AFUNIXSocketException("Error while getting option", e); + } + } + + @Override + public void setOption(int optID, Object value) throws SocketException { + try { + switch (optID) { + case SocketOptions.SO_LINGER: + + if (value instanceof Boolean) { + final boolean b = (Boolean) value; + if (b) { + throw new SocketException("Only accepting Boolean.FALSE here"); + } + NativeUnixSocket.setSocketOptionInt(fd, optID, -1); + return; + } + NativeUnixSocket.setSocketOptionInt(fd, optID, expectInteger(value)); + return; + case SocketOptions.SO_RCVBUF: + case SocketOptions.SO_SNDBUF: + case SocketOptions.SO_TIMEOUT: + NativeUnixSocket.setSocketOptionInt(fd, optID, expectInteger(value)); + return; + case SocketOptions.SO_KEEPALIVE: + case SocketOptions.TCP_NODELAY: + NativeUnixSocket.setSocketOptionInt(fd, optID, expectBoolean(value)); + return; + default: + throw new AFUNIXSocketException("Unsupported option: " + optID); + } + } catch (final AFUNIXSocketException e) { + throw e; + } catch (final Exception e) { + throw new AFUNIXSocketException("Error while setting option", e); + } + } + + @Override + protected void shutdownInput() throws IOException { + if (!closed && fd.valid()) { + NativeUnixSocket.shutdown(fd, SHUT_RD); + } + } + + @Override + protected void shutdownOutput() throws IOException { + if (!closed && fd.valid()) { + NativeUnixSocket.shutdown(fd, SHUT_WR); + } + } + + /** + * Changes the behavior to be somewhat lenient with respect to the specification. + * + * In particular, we ignore calls to {@link Socket#getTcpNoDelay()} and + * {@link Socket#setTcpNoDelay(boolean)}. + */ + static class Lenient extends AFUNIXSocketImpl { + Lenient() { + super(); + } + + @Override + public void setOption(int optID, Object value) throws SocketException { + try { + super.setOption(optID, value); + } catch (SocketException e) { + switch (optID) { + case SocketOptions.TCP_NODELAY: + return; + default: + throw e; + } + } + } + + @Override + public Object getOption(int optID) throws SocketException { + try { + return super.getOption(optID); + } catch (SocketException e) { + switch (optID) { + case SocketOptions.TCP_NODELAY: + case SocketOptions.SO_KEEPALIVE: + return false; + default: + throw e; + } + } + } + } +} diff --git a/src/test/java/com/github/dockerjava/api/ModelsSerializableTest.java b/src/test/java/com/github/dockerjava/api/ModelsSerializableTest.java new file mode 100644 index 000000000..770b136c9 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/ModelsSerializableTest.java @@ -0,0 +1,63 @@ +package com.github.dockerjava.api; + +import com.github.dockerjava.api.model.Binds; +import com.github.dockerjava.api.model.BuildResponseItem; +import com.github.dockerjava.api.model.PullResponseItem; +import com.github.dockerjava.api.model.PushResponseItem; +import com.github.dockerjava.api.model.ResponseItem; +import com.google.common.reflect.ClassPath.ClassInfo; +import org.apache.commons.lang.reflect.FieldUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; + +import static com.google.common.reflect.ClassPath.from; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.object.IsCompatibleType.typeCompatibleWith; +import static org.junit.Assert.assertThat; + +/** + * @author Kanstantsin Shautsou + */ +public class ModelsSerializableTest { + private static final Logger LOG = LoggerFactory.getLogger(ModelsSerializableTest.class); + + private List excludeClasses = Arrays.asList( + Binds.class.getName(), + BuildResponseItem.class.getName(), + PullResponseItem.class.getName(), + PushResponseItem.class.getName(), + ResponseItem.class.getName() + ); + + @Test + public void allModelsSerializable() throws IOException, NoSuchFieldException, IllegalAccessException { + final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + for (ClassInfo classInfo : from(contextClassLoader).getTopLevelClasses("com.github.dockerjava.api.model")) { + if (classInfo.getName().endsWith("Test")) { + continue; + } + + final Class aClass = classInfo.load(); + if (aClass.getProtectionDomain().getCodeSource().getLocation().getPath().endsWith("test-classes/") + || aClass.isEnum()) { + continue; + } + + LOG.debug("aClass: {}", aClass); + assertThat(aClass, typeCompatibleWith(Serializable.class)); + + final Object serialVersionUID = FieldUtils.readDeclaredStaticField(aClass, "serialVersionUID", true); + if (!excludeClasses.contains(aClass.getName())) { + assertThat(serialVersionUID, instanceOf(Long.class)); + assertThat("Follow devel docs", (Long) serialVersionUID, is(1L)); + } + } + } +} diff --git a/src/test/java/com/github/dockerjava/api/command/InspectContainerResponseTest.java b/src/test/java/com/github/dockerjava/api/command/InspectContainerResponseTest.java index f64cf4ead..cd4562b4a 100644 --- a/src/test/java/com/github/dockerjava/api/command/InspectContainerResponseTest.java +++ b/src/test/java/com/github/dockerjava/api/command/InspectContainerResponseTest.java @@ -22,6 +22,7 @@ import static com.github.dockerjava.test.serdes.JSONTestHelper.testRoundTrip; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.IsNot.not; @@ -51,6 +52,7 @@ public void roundTrip_full() throws IOException { assertEquals(response.getVolumesRW()[1].getVolume().getPath(), "/bar/foo/myvol2"); assertFalse(response.getVolumesRW()[1].getAccessMode().toBoolean()); assertTrue(response.getVolumesRW()[0].getAccessMode().toBoolean()); + assertThat(response.getLogPath(), is("/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1-json.log")); } @Test diff --git a/src/test/java/com/github/dockerjava/api/model/BindTest.java b/src/test/java/com/github/dockerjava/api/model/BindTest.java index 3204ee716..2e6fd8353 100644 --- a/src/test/java/com/github/dockerjava/api/model/BindTest.java +++ b/src/test/java/com/github/dockerjava/api/model/BindTest.java @@ -3,6 +3,7 @@ import static com.github.dockerjava.api.model.AccessMode.ro; import static com.github.dockerjava.api.model.AccessMode.rw; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; import org.testng.annotations.Test; @@ -16,6 +17,8 @@ public void parseUsingDefaultAccessMode() { assertThat(bind.getVolume().getPath(), is("/container")); assertThat(bind.getAccessMode(), is(AccessMode.DEFAULT)); assertThat(bind.getSecMode(), is(SELContext.none)); + assertThat(bind.getNoCopy(), nullValue()); + assertThat(bind.getPropagationMode(), is(PropagationMode.DEFAULT_MODE)); } @Test @@ -25,6 +28,52 @@ public void parseReadWrite() { assertThat(bind.getVolume().getPath(), is("/container")); assertThat(bind.getAccessMode(), is(rw)); assertThat(bind.getSecMode(), is(SELContext.none)); + assertThat(bind.getNoCopy(), nullValue()); + assertThat(bind.getPropagationMode(), is(PropagationMode.DEFAULT_MODE)); + } + + @Test + public void parseReadWriteNoCopy() { + Bind bind = Bind.parse("/host:/container:rw,nocopy"); + assertThat(bind.getPath(), is("/host")); + assertThat(bind.getVolume().getPath(), is("/container")); + assertThat(bind.getAccessMode(), is(rw)); + assertThat(bind.getSecMode(), is(SELContext.none)); + assertThat(bind.getNoCopy(), is(true)); + assertThat(bind.getPropagationMode(), is(PropagationMode.DEFAULT_MODE)); + } + + @Test + public void parseReadWriteShared() { + Bind bind = Bind.parse("/host:/container:rw,shared"); + assertThat(bind.getPath(), is("/host")); + assertThat(bind.getVolume().getPath(), is("/container")); + assertThat(bind.getAccessMode(), is(rw)); + assertThat(bind.getSecMode(), is(SELContext.none)); + assertThat(bind.getNoCopy(), nullValue()); + assertThat(bind.getPropagationMode(), is(PropagationMode.SHARED)); + } + + @Test + public void parseReadWriteSlave() { + Bind bind = Bind.parse("/host:/container:rw,slave"); + assertThat(bind.getPath(), is("/host")); + assertThat(bind.getVolume().getPath(), is("/container")); + assertThat(bind.getAccessMode(), is(rw)); + assertThat(bind.getSecMode(), is(SELContext.none)); + assertThat(bind.getNoCopy(), nullValue()); + assertThat(bind.getPropagationMode(), is(PropagationMode.SLAVE)); + } + + @Test + public void parseReadWritePrivate() { + Bind bind = Bind.parse("/host:/container:rw,private"); + assertThat(bind.getPath(), is("/host")); + assertThat(bind.getVolume().getPath(), is("/container")); + assertThat(bind.getAccessMode(), is(rw)); + assertThat(bind.getSecMode(), is(SELContext.none)); + assertThat(bind.getNoCopy(), nullValue()); + assertThat(bind.getPropagationMode(), is(PropagationMode.PRIVATE)); } @Test @@ -34,6 +83,8 @@ public void parseReadOnly() { assertThat(bind.getVolume().getPath(), is("/container")); assertThat(bind.getAccessMode(), is(ro)); assertThat(bind.getSecMode(), is(SELContext.none)); + assertThat(bind.getNoCopy(), nullValue()); + assertThat(bind.getPropagationMode(), is(PropagationMode.DEFAULT_MODE)); } @Test @@ -43,12 +94,16 @@ public void parseSELOnly() { assertThat(bind.getVolume().getPath(), is("/container")); assertThat(bind.getAccessMode(), is(AccessMode.DEFAULT)); assertThat(bind.getSecMode(), is(SELContext.single)); + assertThat(bind.getNoCopy(), nullValue()); + assertThat(bind.getPropagationMode(), is(PropagationMode.DEFAULT_MODE)); bind = Bind.parse("/host:/container:z"); assertThat(bind.getPath(), is("/host")); assertThat(bind.getVolume().getPath(), is("/container")); assertThat(bind.getAccessMode(), is(AccessMode.DEFAULT)); assertThat(bind.getSecMode(), is(SELContext.shared)); + assertThat(bind.getNoCopy(), nullValue()); + assertThat(bind.getPropagationMode(), is(PropagationMode.DEFAULT_MODE)); } @Test @@ -58,6 +113,8 @@ public void parseReadWriteSEL() { assertThat(bind.getVolume().getPath(), is("/container")); assertThat(bind.getAccessMode(), is(rw)); assertThat(bind.getSecMode(), is(SELContext.single)); + assertThat(bind.getNoCopy(), nullValue()); + assertThat(bind.getPropagationMode(), is(PropagationMode.DEFAULT_MODE)); } @Test @@ -67,6 +124,8 @@ public void parseReadOnlySEL() { assertThat(bind.getVolume().getPath(), is("/container")); assertThat(bind.getAccessMode(), is(ro)); assertThat(bind.getSecMode(), is(SELContext.shared)); + assertThat(bind.getNoCopy(), nullValue()); + assertThat(bind.getPropagationMode(), is(PropagationMode.DEFAULT_MODE)); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Error parsing Bind.*") @@ -94,6 +153,26 @@ public void toStringReadWrite() { assertThat(Bind.parse("/host:/container:rw").toString(), is("/host:/container:rw")); } + @Test + public void toStringReadWriteNoCopy() { + assertThat(Bind.parse("/host:/container:rw,nocopy").toString(), is("/host:/container:rw,nocopy")); + } + + @Test + public void toStringReadWriteShared() { + assertThat(Bind.parse("/host:/container:rw,shared").toString(), is("/host:/container:rw,shared")); + } + + @Test + public void toStringReadWriteSlave() { + assertThat(Bind.parse("/host:/container:rw,slave").toString(), is("/host:/container:rw,slave")); + } + + @Test + public void toStringReadWritePrivate() { + assertThat(Bind.parse("/host:/container:rw,private").toString(), is("/host:/container:rw,private")); + } + @Test public void toStringDefaultAccessMode() { assertThat(Bind.parse("/host:/container").toString(), is("/host:/container:rw")); diff --git a/src/test/java/com/github/dockerjava/api/model/DeviceTest.java b/src/test/java/com/github/dockerjava/api/model/DeviceTest.java new file mode 100644 index 000000000..5c48c4886 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/DeviceTest.java @@ -0,0 +1,95 @@ +package com.github.dockerjava.api.model; + +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static junit.framework.Assert.fail; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +/** + * @author Kanstantsin Shautsou + */ +public class DeviceTest { + + public static List validPaths = Arrays.asList( + "/home", + "/home:/home", + "/home:/something/else", + "/with space", + "/home:/with space", + "relative:/absolute-path", + "hostPath:/containerPath:r", + "/hostPath:/containerPath:rw", + "/hostPath:/containerPath:mrw" + ); + + public static HashMap badPaths = new LinkedHashMap() {{ + put("", "bad format for path: "); + // TODO implement ValidatePath +// put("./", "./ is not an absolute path"); +// put("../", "../ is not an absolute path"); +// put("/:../", "../ is not an absolute path"); +// put("/:path", "path is not an absolute path"); +// put(":", "bad format for path: :"); +// put("/tmp:", " is not an absolute path"); +// put(":test", "bad format for path: :test"); +// put(":/test", "bad format for path: :/test"); +// put("tmp:", " is not an absolute path"); +// put(":test:", "bad format for path: :test:"); +// put("::", "bad format for path: ::"); +// put(":::", "bad format for path: :::"); +// put("/tmp:::", "bad format for path: /tmp:::"); +// put(":/tmp::", "bad format for path: :/tmp::"); +// put("path:ro", "ro is not an absolute path"); +// put("path:rr", "rr is not an absolute path"); + put("a:/b:ro", "bad mode specified: ro"); + put("a:/b:rr", "bad mode specified: rr"); + }}; + + @Test + public void testParse() throws Exception { + assertThat(Device.parse("/dev/sda:/dev/xvdc:r"), + equalTo(new Device("r", "/dev/xvdc", "/dev/sda"))); + + assertThat(Device.parse("/dev/snd:rw"), + equalTo(new Device("rw", "/dev/snd", "/dev/snd"))); + + assertThat(Device.parse("/dev/snd:/something"), + equalTo(new Device("rwm", "/something", "/dev/snd"))); + + assertThat(Device.parse("/dev/snd:/something:rw"), + equalTo(new Device("rw", "/something", "/dev/snd"))); + + } + + @Test + public void testParseBadPaths() { + for (Map.Entry entry : badPaths.entrySet()) { + final String deviceStr = entry.getKey(); + try { + Device.parse(deviceStr); + fail("Should fail because: " + entry.getValue() + " '" + deviceStr + "'"); + } catch (IllegalArgumentException ex) { + assertThat(ex.getMessage(), containsString("Invalid device specification:")); + } + } + } + + @Test + public void testParseValidPaths() { + for (String path : validPaths) { + Device.parse(path); + } + } +} diff --git a/src/test/java/com/github/dockerjava/api/model/EventsTest.java b/src/test/java/com/github/dockerjava/api/model/EventsTest.java new file mode 100644 index 000000000..ce62c7d5f --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/EventsTest.java @@ -0,0 +1,64 @@ +package com.github.dockerjava.api.model; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.core.RemoteApiVersion; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.HashMap; + +import static com.github.dockerjava.api.model.EventType.CONTAINER; +import static com.github.dockerjava.test.serdes.JSONSamples.testRoundTrip; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; + +/** + * @author Kanstantsin Shautsou + */ +public class EventsTest { + + @Test + public void serderDocs1() throws IOException { + final ObjectMapper mapper = new ObjectMapper(); + final JavaType type = mapper.getTypeFactory().uncheckedSimpleType(Event.class); + + final Event event = testRoundTrip(RemoteApiVersion.VERSION_1_24, + "/events/docs1.json", + type + ); + + assertThat(event, notNullValue()); + assertThat(event.getType(), is(CONTAINER)); + assertThat(event.getAction(), is("create")); + assertThat(event.getId(), is("ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743")); + assertThat(event.getFrom(), is("alpine")); + assertThat(event.getTime(), is(1461943101L)); + assertThat(event.getNode(), nullValue()); + assertThat(event.getTimeNano(), is(1461943101381709551L)); + + final HashMap attributes = new HashMap<>(); + attributes.put("com.example.some-label", "some-label-value"); + attributes.put("image", "alpine"); + attributes.put("name", "my-container"); + + final EventActor actor = new EventActor() + .withId("ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743") + .withAttributes(attributes); + + final Event event1 = new Event() + .withType(CONTAINER) + .withStatus("create") + .withId("ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743") + .withFrom("alpine") + .withTime(1461943101L) + .withTimenano(1461943101381709551L) + .withAction("create") + .withEventActor(actor); + + assertThat(event1, equalTo(event)); + } +} diff --git a/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java b/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java index 4c8750ecc..e6cc00ac0 100644 --- a/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java +++ b/src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java @@ -29,7 +29,7 @@ import com.github.dockerjava.api.model.Network; import com.github.dockerjava.api.model.Volume; import com.github.dockerjava.core.DockerClientBuilder; -import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.TestDockerCmdExecFactory; import com.github.dockerjava.core.command.BuildImageResultCallback; import com.github.dockerjava.core.command.LogContainerResultCallback; @@ -68,12 +68,12 @@ public void beforeTest() throws Exception { LOG.info("======================= END OF BEFORETEST =======================\n\n"); } - private DockerClientConfig config() { + private DefaultDockerClientConfig config() { return config(null); } - protected DockerClientConfig config(String password) { - DockerClientConfig.DockerClientConfigBuilder builder = DockerClientConfig.createDefaultConfigBuilder() + protected DefaultDockerClientConfig config(String password) { + DefaultDockerClientConfig.Builder builder = DefaultDockerClientConfig.createDefaultConfigBuilder() .withRegistryUrl("https://index.docker.io/v1/"); if (password != null) { builder = builder.withRegistryPassword(password); @@ -225,17 +225,33 @@ protected String containerLog(String containerId) throws Exception { public static class LogContainerTestCallback extends LogContainerResultCallback { protected final StringBuffer log = new StringBuffer(); + List collectedFrames = new ArrayList(); + + boolean collectFrames = false; + + public LogContainerTestCallback() { + this(false); + } + + public LogContainerTestCallback(boolean collectFrames) { + this.collectFrames = collectFrames; + } + @Override public void onNext(Frame frame) { + if(collectFrames) collectedFrames.add(frame); log.append(new String(frame.getPayload())); - System.err.println("LogContainerTestCallback: " + log.toString()); - // super.onNext(frame); } @Override public String toString() { return log.toString(); } + + + public List getCollectedFrames() { + return collectedFrames; + } } protected String buildImage(File baseDir) throws Exception { diff --git a/src/test/java/com/github/dockerjava/core/DefaultDockerClientConfigTest.java b/src/test/java/com/github/dockerjava/core/DefaultDockerClientConfigTest.java new file mode 100644 index 000000000..54e70c511 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/DefaultDockerClientConfigTest.java @@ -0,0 +1,204 @@ +package com.github.dockerjava.core; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.core.Is.is; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import java.lang.reflect.Field; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.lang.SerializationUtils; +import org.testng.annotations.Test; + +import com.github.dockerjava.api.exception.DockerClientException; +import com.github.dockerjava.api.model.AuthConfig; + +public class DefaultDockerClientConfigTest { + + public static final DefaultDockerClientConfig EXAMPLE_CONFIG = newExampleConfig(); + + private static DefaultDockerClientConfig newExampleConfig() { + + String dockerCertPath = dockerCertPath(); + + return new DefaultDockerClientConfig(URI.create("tcp://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", + new LocalDirectorySSLConfig(dockerCertPath)); + } + + private static String homeDir() { + return "target/test-classes/someHomeDir"; + } + + private static String dockerCertPath() { + return homeDir() + "/.docker"; + } + + @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(DefaultDockerClientConfig.DOCKER_HOST, "tcp://baz:8768"); + // and it looks to be SSL disabled + env.remove("DOCKER_CERT_PATH"); + + // given default cert path + Properties systemProperties = new Properties(); + systemProperties.setProperty("user.name", "someUserName"); + systemProperties.setProperty("user.home", homeDir()); + + // when you build a config + DefaultDockerClientConfig config = buildConfig(env, systemProperties); + + assertEquals(config.getDockerHost(), URI.create("tcp://baz:8768")); + } + + @Test + public void environment() throws Exception { + + // given a default config in env properties + Map env = new HashMap(); + env.put(DefaultDockerClientConfig.DOCKER_HOST, "tcp://foo"); + env.put(DefaultDockerClientConfig.API_VERSION, "apiVersion"); + env.put(DefaultDockerClientConfig.REGISTRY_USERNAME, "registryUsername"); + env.put(DefaultDockerClientConfig.REGISTRY_PASSWORD, "registryPassword"); + env.put(DefaultDockerClientConfig.REGISTRY_EMAIL, "registryEmail"); + env.put(DefaultDockerClientConfig.REGISTRY_URL, "registryUrl"); + env.put(DefaultDockerClientConfig.DOCKER_CONFIG, "dockerConfig"); + env.put(DefaultDockerClientConfig.DOCKER_CERT_PATH, dockerCertPath()); + env.put(DefaultDockerClientConfig.DOCKER_TLS_VERIFY, "1"); + + // when you build a config + DefaultDockerClientConfig config = buildConfig(env, new Properties()); + + // then we get the example object + assertEquals(config, EXAMPLE_CONFIG); + } + + private DefaultDockerClientConfig buildConfig(Map env, Properties systemProperties) { + return DefaultDockerClientConfig.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", homeDir()); + + // when you build config + DefaultDockerClientConfig config = buildConfig(Collections. emptyMap(), systemProperties); + + // then the cert path is as expected + assertEquals(config.getDockerHost(), URI.create("unix:///var/run/docker.sock")); + assertEquals(config.getRegistryUsername(), "someUserName"); + assertEquals(config.getRegistryUrl(), AuthConfig.DEFAULT_SERVER_ADDRESS); + assertEquals(config.getApiVersion(), RemoteApiVersion.unknown()); + assertEquals(config.getDockerConfig(), homeDir() + "/.docker"); + assertNull(config.getSSLConfig()); + } + + @Test + public void systemProperties() throws Exception { + + // given system properties based on the example + Properties systemProperties = new Properties(); + systemProperties.put(DefaultDockerClientConfig.DOCKER_HOST, "tcp://foo"); + systemProperties.put(DefaultDockerClientConfig.API_VERSION, "apiVersion"); + systemProperties.put(DefaultDockerClientConfig.REGISTRY_USERNAME, "registryUsername"); + systemProperties.put(DefaultDockerClientConfig.REGISTRY_PASSWORD, "registryPassword"); + systemProperties.put(DefaultDockerClientConfig.REGISTRY_EMAIL, "registryEmail"); + systemProperties.put(DefaultDockerClientConfig.REGISTRY_URL, "registryUrl"); + systemProperties.put(DefaultDockerClientConfig.DOCKER_CONFIG, "dockerConfig"); + systemProperties.put(DefaultDockerClientConfig.DOCKER_CERT_PATH, dockerCertPath()); + systemProperties.put(DefaultDockerClientConfig.DOCKER_TLS_VERIFY, "1"); + + // when you build new config + DefaultDockerClientConfig config = buildConfig(Collections. emptyMap(), systemProperties); + + // then it is the same as the example + assertEquals(config, EXAMPLE_CONFIG); + + } + + @Test + public void serializableTest() { + final byte[] serialized = SerializationUtils.serialize(EXAMPLE_CONFIG); + final DefaultDockerClientConfig deserialized = (DefaultDockerClientConfig) SerializationUtils.deserialize(serialized); + + assertThat("Deserialized object mush match source object", deserialized, equalTo(EXAMPLE_CONFIG)); + } + + @Test() + public void testSslContextEmpty() throws Exception { + new DefaultDockerClientConfig(URI.create("tcp://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", + null); + } + + + + @Test() + public void testTlsVerifyAndCertPath() throws Exception { + new DefaultDockerClientConfig(URI.create("tcp://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", + new LocalDirectorySSLConfig(dockerCertPath())); + } + + @Test(expectedExceptions = DockerClientException.class) + public void testWrongHostScheme() throws Exception { + new DefaultDockerClientConfig(URI.create("http://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", + null); + } + + @Test() + public void testTcpHostScheme() throws Exception { + new DefaultDockerClientConfig(URI.create("tcp://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", + null); + } + + @Test() + public void testUnixHostScheme() throws Exception { + new DefaultDockerClientConfig(URI.create("unix://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", + null); + } + + @Test + public void withDockerTlsVerify() throws Exception { + DefaultDockerClientConfig.Builder builder = new DefaultDockerClientConfig.Builder(); + Field field = builder.getClass().getDeclaredField("dockerTlsVerify"); + field.setAccessible(true); + + builder.withDockerTlsVerify(""); + assertThat((Boolean) field.get(builder), is(false)); + + builder.withDockerTlsVerify("false"); + assertThat((Boolean) field.get(builder), is(false)); + + builder.withDockerTlsVerify("FALSE"); + assertThat((Boolean) field.get(builder), is(false)); + + builder.withDockerTlsVerify("true"); + assertThat((Boolean) field.get(builder), is(true)); + + builder.withDockerTlsVerify("TRUE"); + assertThat((Boolean) field.get(builder), is(true)); + + builder.withDockerTlsVerify("0"); + assertThat((Boolean) field.get(builder), is(false)); + + builder.withDockerTlsVerify("1"); + assertThat((Boolean) field.get(builder), is(true)); + } + +} diff --git a/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java b/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java deleted file mode 100644 index ee679a0af..000000000 --- a/src/test/java/com/github/dockerjava/core/DockerClientConfigTest.java +++ /dev/null @@ -1,208 +0,0 @@ -package com.github.dockerjava.core; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.core.Is.is; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; - -import java.lang.reflect.Field; -import java.net.URI; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import org.apache.commons.lang.SerializationUtils; -import org.testng.annotations.Test; - -import com.github.dockerjava.api.exception.DockerClientException; -import com.github.dockerjava.api.model.AuthConfig; - -public class DockerClientConfigTest { - - public static final DockerClientConfig EXAMPLE_CONFIG = newExampleConfig(); - - private static DockerClientConfig newExampleConfig() { - - String dockerCertPath = dockerCertPath(); - - return new DockerClientConfig(URI.create("tcp://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", - dockerCertPath, true); - } - - private static String homeDir() { - return "target/test-classes/someHomeDir"; - } - - private static String dockerCertPath() { - return homeDir() + "/.docker"; - } - - @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(DockerClientConfig.DOCKER_HOST, "tcp://baz:8768"); - // and it looks to be SSL disabled - env.remove("DOCKER_CERT_PATH"); - - // given default cert path - Properties systemProperties = new Properties(); - systemProperties.setProperty("user.name", "someUserName"); - systemProperties.setProperty("user.home", homeDir()); - - // when you build a config - DockerClientConfig config = buildConfig(env, systemProperties); - - assertEquals(config.getDockerHost(), URI.create("tcp://baz:8768")); - } - - @Test - public void environment() throws Exception { - - // given a default config in env properties - Map env = new HashMap(); - env.put(DockerClientConfig.DOCKER_HOST, "tcp://foo"); - env.put(DockerClientConfig.API_VERSION, "apiVersion"); - env.put(DockerClientConfig.REGISTRY_USERNAME, "registryUsername"); - env.put(DockerClientConfig.REGISTRY_PASSWORD, "registryPassword"); - env.put(DockerClientConfig.REGISTRY_EMAIL, "registryEmail"); - env.put(DockerClientConfig.REGISTRY_URL, "registryUrl"); - env.put(DockerClientConfig.DOCKER_CONFIG, "dockerConfig"); - env.put(DockerClientConfig.DOCKER_CERT_PATH, dockerCertPath()); - env.put(DockerClientConfig.DOCKER_TLS_VERIFY, "1"); - - // 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", homeDir()); - - // when you build config - DockerClientConfig config = buildConfig(Collections. emptyMap(), systemProperties); - - // then the cert path is as expected - assertEquals(config.getDockerHost(), URI.create("unix:///var/run/docker.sock")); - assertEquals(config.getRegistryUsername(), "someUserName"); - assertEquals(config.getRegistryUrl(), AuthConfig.DEFAULT_SERVER_ADDRESS); - assertEquals(config.getApiVersion(), RemoteApiVersion.unknown()); - assertEquals(config.getDockerConfig(), homeDir() + "/.docker"); - assertNull(config.getDockerCertPath()); - } - - @Test - public void systemProperties() throws Exception { - - // given system properties based on the example - Properties systemProperties = new Properties(); - systemProperties.put(DockerClientConfig.DOCKER_HOST, "tcp://foo"); - systemProperties.put(DockerClientConfig.API_VERSION, "apiVersion"); - systemProperties.put(DockerClientConfig.REGISTRY_USERNAME, "registryUsername"); - systemProperties.put(DockerClientConfig.REGISTRY_PASSWORD, "registryPassword"); - systemProperties.put(DockerClientConfig.REGISTRY_EMAIL, "registryEmail"); - systemProperties.put(DockerClientConfig.REGISTRY_URL, "registryUrl"); - systemProperties.put(DockerClientConfig.DOCKER_CONFIG, "dockerConfig"); - systemProperties.put(DockerClientConfig.DOCKER_CERT_PATH, dockerCertPath()); - systemProperties.put(DockerClientConfig.DOCKER_TLS_VERIFY, "1"); - - // when you build new config - DockerClientConfig config = buildConfig(Collections. emptyMap(), systemProperties); - - // then it is the same as the example - assertEquals(config, EXAMPLE_CONFIG); - - } - - @Test - public void serializableTest() { - final byte[] serialized = SerializationUtils.serialize(EXAMPLE_CONFIG); - final DockerClientConfig deserialized = (DockerClientConfig) SerializationUtils.deserialize(serialized); - - assertThat("Deserialized object mush match source object", deserialized, equalTo(EXAMPLE_CONFIG)); - } - - @Test(expectedExceptions = DockerClientException.class) - public void testTlsVerifyAndCertPathNull() throws Exception { - new DockerClientConfig(URI.create("tcp://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", - null, true); - } - - @Test(expectedExceptions = DockerClientException.class) - public void testTlsVerifyAndCertPathEmpty() throws Exception { - new DockerClientConfig(URI.create("tcp://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", - "", true); - } - - @Test() - public void testTlsVerifyAndCertPath() throws Exception { - new DockerClientConfig(URI.create("tcp://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", - dockerCertPath(), true); - } - - @Test(expectedExceptions = DockerClientException.class) - public void testWrongHostScheme() throws Exception { - new DockerClientConfig(URI.create("http://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", - null, false); - } - - @Test() - public void testTcpHostScheme() throws Exception { - new DockerClientConfig(URI.create("tcp://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", - null, false); - } - - @Test() - public void testUnixHostScheme() throws Exception { - new DockerClientConfig(URI.create("unix://foo"), "dockerConfig", "apiVersion", "registryUrl", "registryUsername", "registryPassword", "registryEmail", - null, false); - } - - @Test - public void withDockerTlsVerify() throws Exception { - DockerClientConfig.DockerClientConfigBuilder builder = new DockerClientConfig.DockerClientConfigBuilder(); - Field field = builder.getClass().getDeclaredField("dockerTlsVerify"); - field.setAccessible(true); - - builder.withDockerTlsVerify(""); - assertThat(field.getBoolean(builder), is(false)); - - builder.withDockerTlsVerify("false"); - assertThat(field.getBoolean(builder), is(false)); - - builder.withDockerTlsVerify("FALSE"); - assertThat(field.getBoolean(builder), is(false)); - - builder.withDockerTlsVerify("true"); - assertThat(field.getBoolean(builder), is(true)); - - builder.withDockerTlsVerify("TRUE"); - assertThat(field.getBoolean(builder), is(true)); - - builder.withDockerTlsVerify("0"); - assertThat(field.getBoolean(builder), is(false)); - - builder.withDockerTlsVerify("1"); - assertThat(field.getBoolean(builder), is(true)); - } - -} diff --git a/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java b/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java index 7ea8bace5..1df4636ba 100644 --- a/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java +++ b/src/test/java/com/github/dockerjava/core/DockerClientImplTest.java @@ -12,7 +12,7 @@ public class DockerClientImplTest { @Test public void configuredInstanceAuthConfig() throws Exception { // given a config with null serverAddress - DockerClientConfig dockerClientConfig = new DockerClientConfig(URI.create("tcp://foo"), null, null, null, "", "", "", null, false); + DefaultDockerClientConfig dockerClientConfig = new DefaultDockerClientConfig(URI.create("tcp://foo"), null, null, null, "", "", "", null); DockerClientImpl dockerClient = DockerClientImpl.getInstance(dockerClientConfig); // when we get the auth config diff --git a/src/test/java/com/github/dockerjava/core/GoLangFileMatchTest.java b/src/test/java/com/github/dockerjava/core/GoLangFileMatchTest.java index 0fd870194..62b342595 100644 --- a/src/test/java/com/github/dockerjava/core/GoLangFileMatchTest.java +++ b/src/test/java/com/github/dockerjava/core/GoLangFileMatchTest.java @@ -5,13 +5,14 @@ import java.io.IOException; -import com.github.dockerjava.core.exception.GoLangFileMatchException; -import junit.framework.Assert; - import org.apache.commons.io.FilenameUtils; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import com.github.dockerjava.core.exception.GoLangFileMatchException; + +import junit.framework.Assert; + public class GoLangFileMatchTest { @Test(dataProvider = "getTestData") @@ -31,7 +32,7 @@ public void testMatch(MatchTestCase testCase) throws IOException { if (testCase.expectException) { Assert.fail("Expected GoFileMatchException"); } - Assert.assertEquals(testCase.matches, matched); + Assert.assertEquals(testCase.toString(), testCase.matches, matched); } catch (GoLangFileMatchException e) { if (!testCase.expectException) { throw e; @@ -41,12 +42,13 @@ public void testMatch(MatchTestCase testCase) throws IOException { @DataProvider public Object[][] getTestData() { - return new Object[][] {new Object[] {new MatchTestCase("abc", "abc", true, false)}, + return new Object[][] {new Object[] {new MatchTestCase("", "abc", false, false)}, + 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*", "ab/c", true, 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)}, @@ -92,9 +94,13 @@ public Object[][] getTestData() { 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[", "a", false, true)}, new Object[] {new MatchTestCase("a[", "ab", false, true)}, - new Object[] {new MatchTestCase("*x", "xxx", true, false)}}; + new Object[] {new MatchTestCase("*x", "xxx", true, false)}, + new Object[] {new MatchTestCase("a", "a/b/c", true, false)}, + new Object[] {new MatchTestCase("*/b", "a/b/c", true, false)}, + new Object[] {new MatchTestCase("**/b/*/d", "a/b/c/d", true, false)}, + new Object[] {new MatchTestCase("**/c", "a/b/c", true, false)}}; } private final class MatchTestCase { diff --git a/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java b/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java index aee8a60a8..46d6d4dcc 100644 --- a/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java +++ b/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java @@ -34,6 +34,7 @@ import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.command.ListNetworksCmd; import com.github.dockerjava.api.command.ListVolumesCmd; +import com.github.dockerjava.api.command.LoadImageCmd; import com.github.dockerjava.api.command.LogContainerCmd; import com.github.dockerjava.api.command.PauseContainerCmd; import com.github.dockerjava.api.command.PingCmd; @@ -58,7 +59,6 @@ import com.github.dockerjava.api.command.WaitContainerCmd; import com.github.dockerjava.api.model.BuildResponseItem; -import javax.net.ssl.SSLContext; import java.io.IOException; import java.security.SecureRandom; import java.util.ArrayList; @@ -132,6 +132,17 @@ public CreateImageResponse exec(CreateImageCmd command) { }; } + @Override + public LoadImageCmd.Exec createLoadImageCmdExec() { + return new LoadImageCmd.Exec() { + @Override + public Void exec(LoadImageCmd command) { + delegate.createLoadImageCmdExec().exec(command); + return null; + } + }; + } + @Override public RemoveImageCmd.Exec createRemoveImageCmdExec() { return new RemoveImageCmd.Exec() { @@ -431,9 +442,4 @@ public List getVolumeNames() { public List getNetworkIds() { return new ArrayList<>(networkIds); } - - @Override - public DockerCmdExecFactory withSSLContext(SSLContext sslContext) { - return delegate.withSSLContext(sslContext); - } } diff --git a/src/test/java/com/github/dockerjava/core/command/AttachContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/AttachContainerCmdImplTest.java index 21507e44e..ef69984f3 100644 --- a/src/test/java/com/github/dockerjava/core/command/AttachContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/AttachContainerCmdImplTest.java @@ -1,5 +1,6 @@ package com.github.dockerjava.core.command; +import static org.apache.commons.lang.StringUtils.isEmpty; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.isEmptyString; @@ -11,7 +12,9 @@ import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; +import org.apache.commons.codec.binary.StringUtils; import org.testng.ITestResult; +import org.testng.SkipException; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeMethod; @@ -97,14 +100,17 @@ public void onNext(Frame frame) { }; }; - dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true) - .exec(callback).awaitCompletion(15, TimeUnit.SECONDS); + dockerClient.attachContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .withFollowStream(true) + .exec(callback) + .awaitCompletion(15, TimeUnit.SECONDS); callback.close(); System.out.println("log: " + callback.toString()); // HexDump.dump(collectFramesCallback.toString().getBytes(), 0, System.out, 0); - assertThat(callback.toString(), containsString("stdout\r\nstderr")); } @@ -145,6 +151,11 @@ public void onNext(Frame item) { super.onNext(item); } + @Override + public RuntimeException getFirstError() { + return super.getFirstError(); + } + @Override public String toString() { return log.toString(); diff --git a/src/test/java/com/github/dockerjava/core/command/AuthCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/AuthCmdImplTest.java index c01c8b8cd..887c99130 100644 --- a/src/test/java/com/github/dockerjava/core/command/AuthCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/AuthCmdImplTest.java @@ -45,14 +45,9 @@ public void testAuth() throws Exception { } // Disabled because of 500/InternalServerException - @Test(enabled = false) + @Test(enabled = false, expectedExceptions = UnauthorizedException.class, expectedExceptionsMessageRegExp = "Wrong login/password, please try again") 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"); - } + DockerClientBuilder.getInstance(config("garbage")).build().authCmd().exec(); } } diff --git a/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java index 950b80a24..cb4c1b2a8 100644 --- a/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/BuildImageCmdImplTest.java @@ -1,11 +1,11 @@ package com.github.dockerjava.core.command; +import static com.github.dockerjava.utils.TestUtils.getVersion; 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; @@ -13,11 +13,13 @@ import java.io.InputStream; import java.lang.reflect.Method; import java.util.Collection; +import java.util.Collections; import java.util.UUID; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.TrueFileFilter; import org.testng.ITestResult; +import org.testng.SkipException; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeMethod; @@ -26,7 +28,6 @@ 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.exception.DockerClientException; import com.github.dockerjava.api.model.AuthConfig; @@ -35,6 +36,7 @@ import com.github.dockerjava.api.model.PortBinding; import com.github.dockerjava.api.model.Ports.Binding; import com.github.dockerjava.client.AbstractDockerClientTest; +import com.github.dockerjava.core.RemoteApiVersion; import com.github.dockerjava.core.util.CompressArchiveUtil; @Test(groups = "integration") @@ -61,10 +63,9 @@ public void afterMethod(ITestResult result) { } @Test - public void testNginxDockerfileBuilder() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("nginx").getFile()); + public void author() throws Exception { - String imageId = buildImage(baseDir); + String imageId = buildImage(fileFromBuildTestResource("AUTHOR")); InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); assertThat(inspectImageResponse, not(nullValue())); @@ -73,27 +74,9 @@ public void testNginxDockerfileBuilder() throws Exception { assertThat(inspectImageResponse.getAuthor(), equalTo("Guillaume J. Charmes \"guillaume@dotcloud.com\"")); } - @Test(groups = "ignoreInCircleCi") - public void testNonstandard1() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader() - .getResource("nonstandard/subdirectory/Dockerfile-nonstandard").getFile()); - - buildImage(baseDir); - } - - @Test(groups = "ignoreInCircleCi") - public void testNonstandard2() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("nonstandard").getFile()); - File dockerFile = new File(Thread.currentThread().getContextClassLoader() - .getResource("nonstandard/subdirectory/Dockerfile-nonstandard").getFile()); - - dockerClient.buildImageCmd().withBaseDirectory(baseDir).withDockerfile(dockerFile).withNoCache(true) - .exec(new BuildImageResultCallback()).awaitImageId(); - } - @Test - public void testDockerBuilderFromTar() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFile").getFile()); + public void buildImageFromTar() throws Exception { + File baseDir = fileFromBuildTestResource("ADD/file"); Collection files = FileUtils.listFiles(baseDir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); File tarFile = CompressArchiveUtil.archiveTARFiles(baseDir, files, UUID.randomUUID().toString()); String response = dockerfileBuild(new FileInputStream(tarFile)); @@ -101,54 +84,45 @@ public void testDockerBuilderFromTar() throws Exception { } @Test - public void testDockerBuildWithOnBuild() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddOnBuild/onbuild") - .getFile()); + public void onBuild() throws Exception { + File baseDir = fileFromBuildTestResource("ONBUILD/parent"); + dockerClient.buildImageCmd(baseDir).withNoCache(true).withTag("docker-java-onbuild") .exec(new BuildImageResultCallback()).awaitImageId(); - baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddOnBuild/test").getFile()); + baseDir = fileFromBuildTestResource("ONBUILD/child"); String response = dockerfileBuild(baseDir); assertThat(response, containsString("Successfully executed testrun.sh")); } @Test - public void testDockerBuilderAddUrl() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddUrl").getFile()); + public void addUrl() throws Exception { + File baseDir = fileFromBuildTestResource("ADD/url"); String response = dockerfileBuild(baseDir); - assertThat(response, containsString("Docker")); + assertThat(response, containsString("Example Domain")); } @Test - public void testDockerBuilderAddFileInSubfolder() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFileInSubfolder") - .getFile()); + public void addFileInSubfolder() throws Exception { + File baseDir = fileFromBuildTestResource("ADD/fileInSubfolder"); String response = dockerfileBuild(baseDir); assertThat(response, containsString("Successfully executed testrun.sh")); } @Test - public void testDockerBuilderAddFilesViaWildcard() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFilesViaWildcard") - .getFile()); + public void addFilesViaWildcard() throws Exception { + File baseDir = fileFromBuildTestResource("ADD/filesViaWildcard"); String response = dockerfileBuild(baseDir); assertThat(response, containsString("Successfully executed testinclude1.sh")); assertThat(response, not(containsString("Successfully executed testinclude2.sh"))); } @Test - public void testDockerBuilderAddFolder() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFolder").getFile()); + public void addFolder() throws Exception { + File baseDir = fileFromBuildTestResource("ADD/folder"); String response = dockerfileBuild(baseDir); assertThat(response, containsString("Successfully executed testAddFolder.sh")); } - @Test - public void testDockerBuilderEnv() throws Exception { - 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) throws Exception { return execBuild(dockerClient.buildImageCmd().withTarInputStream(tarInputStream)); @@ -175,80 +149,42 @@ private String execBuild(BuildImageCmd buildImageCmd) throws Exception { } @Test(expectedExceptions = {DockerClientException.class}) - public void testDockerfileIgnored() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testDockerfileIgnored") - .getFile()); + public void dockerignoreDockerfileIgnored() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/DockerfileIgnored"); dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()).awaitImageId(); } @Test - public void testDockerfileNotIgnored() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testDockerfileNotIgnored") - .getFile()); + public void dockerignoreDockerfileNotIgnored() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/DockerfileNotIgnored"); dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()).awaitImageId(); } @Test(expectedExceptions = {DockerClientException.class}) - public void testInvalidDockerIgnorePattern() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader() - .getResource("testInvalidDockerignorePattern").getFile()); + public void dockerignoreInvalidDockerIgnorePattern() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/InvalidDockerignorePattern"); dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()).awaitImageId(); } - @Test(groups = "ignoreInCircleCi") - public void testDockerIgnore() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testDockerignore") - .getFile()); + @Test() + public void dockerignoreValidDockerIgnorePattern() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/ValidDockerignorePattern"); String response = dockerfileBuild(baseDir); assertThat(response, containsString("/tmp/a/a /tmp/a/c /tmp/a/d")); } @Test - public void testNetCatDockerfileBuilder() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("netcat").getFile()); - - String imageId = dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()) - .awaitImageId(); - - 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 Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testENVSubstitution") - .getFile()); + public void env() throws Exception { + File baseDir = fileFromBuildTestResource("ENV"); String response = dockerfileBuild(baseDir); assertThat(response, containsString("testENVSubstitution successfully completed")); } @Test - public void testBuildFromPrivateRegistry() throws Exception { + public void fromPrivateRegistry() throws Exception { File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("privateRegistry").getFile()); String imageId = buildImage(baseDir); @@ -289,8 +225,7 @@ public void testBuildFromPrivateRegistry() throws Exception { dockerClient.removeImageCmd("localhost:5000/testuser/busybox").withForce(true).exec(); - baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testBuildFromPrivateRegistry") - .getFile()); + baseDir = fileFromBuildTestResource("FROM/privateRegistry"); AuthConfigurations authConfigurations = new AuthConfigurations(); authConfigurations.addConfig(authConfig); @@ -305,8 +240,8 @@ public void testBuildFromPrivateRegistry() throws Exception { } @Test - public void testBuildArgs() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testBuildArgs").getFile()); + public void buildArgs() throws Exception { + File baseDir = fileFromBuildTestResource("buildArgs"); String imageId = dockerClient.buildImageCmd(baseDir).withNoCache(true).withBuildArg("testArg", "abc") .exec(new BuildImageResultCallback()) @@ -319,9 +254,29 @@ public void testBuildArgs() throws Exception { assertThat(inspectImageResponse.getConfig().getLabels().get("test"), equalTo("abc")); } - public void testDockerfileNotInBaseDirectory() throws Exception { - File baseDirectory = getResource("testDockerfileNotInBaseDirectory"); - File dockerfile = getResource("testDockerfileNotInBaseDirectory/dockerfileFolder/Dockerfile"); + @Test + public void labels() throws Exception { + if (!getVersion(dockerClient).isGreaterOrEqual(RemoteApiVersion.VERSION_1_23)) { + throw new SkipException("API version should be >= 1.23"); + } + + File baseDir = fileFromBuildTestResource("labels"); + + String imageId = dockerClient.buildImageCmd(baseDir).withNoCache(true) + .withLabels(Collections.singletonMap("test", "abc")) + .exec(new BuildImageResultCallback()) + .awaitImageId(); + + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); + assertThat(inspectImageResponse, not(nullValue())); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + assertThat(inspectImageResponse.getConfig().getLabels().get("test"), equalTo("abc")); + } + + public void dockerfileNotInBaseDirectory() throws Exception { + File baseDirectory = fileFromBuildTestResource("dockerfileNotInBaseDirectory"); + File dockerfile = fileFromBuildTestResource("dockerfileNotInBaseDirectory/dockerfileFolder/Dockerfile"); BuildImageCmd command = dockerClient.buildImageCmd() .withBaseDirectory(baseDirectory) .withDockerfile(dockerfile); @@ -331,7 +286,8 @@ public void testDockerfileNotInBaseDirectory() throws Exception { assertThat(response, containsString("Successfully executed testrun.sh")); } - private File getResource(String path) { - return new File(Thread.currentThread().getContextClassLoader().getResource(path).getFile()); + private File fileFromBuildTestResource(String resource) { + return new File(Thread.currentThread().getContextClassLoader() + .getResource("buildTests/" + resource).getFile()); } } diff --git a/src/test/java/com/github/dockerjava/core/command/CommitCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/CommitCmdImplTest.java index b8758d332..64e1d7ddf 100644 --- a/src/test/java/com/github/dockerjava/core/command/CommitCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/CommitCmdImplTest.java @@ -68,13 +68,9 @@ public void commit() throws DockerException { assertThat(inspectImageResponse.getParent(), equalTo(busyboxImg.getId())); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void commitNonExistingContainer() throws DockerException { - try { - dockerClient.commitCmd("non-existent").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } - } + dockerClient.commitCmd("non-existent").exec(); + } } diff --git a/src/test/java/com/github/dockerjava/core/command/ConnectToNetworkCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ConnectToNetworkCmdImplTest.java index 84bb59a81..207d2a0d5 100644 --- a/src/test/java/com/github/dockerjava/core/command/ConnectToNetworkCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/ConnectToNetworkCmdImplTest.java @@ -3,6 +3,7 @@ import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.command.CreateNetworkResponse; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.model.ContainerNetwork; import com.github.dockerjava.api.model.Network; import com.github.dockerjava.client.AbstractDockerClientTest; @@ -16,6 +17,10 @@ import java.lang.reflect.Method; import java.util.Collections; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + @Test(groups = "integration") public class ConnectToNetworkCmdImplTest extends AbstractDockerClientTest { @@ -60,38 +65,49 @@ public void connectToNetwork() throws InterruptedException { @Test public void connectToNetworkWithContainerNetwork() throws InterruptedException { + final String NETWORK_SUBNET = "10.100.102.0/24"; + final String NETWORK_NAME = "jerseyTestNetwork"; + final String CONTAINER_IP = "10.100.102.100"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("sleep", "9999") + .exec(); - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); dockerClient.startContainerCmd(container.getId()).exec(); + try { + dockerClient.removeNetworkCmd(NETWORK_NAME).exec(); + } catch (DockerException ignore) { + } + CreateNetworkResponse network = dockerClient.createNetworkCmd() - .withName("testNetwork") + .withName(NETWORK_NAME) .withIpam(new Network.Ipam() - .withConfig(new Network.Ipam.Config() - .withSubnet("10.100.100.0/24"))) + .withConfig(new Network.Ipam.Config() + .withSubnet(NETWORK_SUBNET))) .exec(); dockerClient.connectToNetworkCmd() .withNetworkId(network.getId()) .withContainerId(container.getId()) .withContainerNetwork(new ContainerNetwork() - .withAliases("testing") - .withIpamConfig(new ContainerNetwork.Ipam() - .withIpv4Address("10.100.100.100"))) + .withAliases("aliasName") + .withIpamConfig(new ContainerNetwork.Ipam() + .withIpv4Address(CONTAINER_IP))) .exec(); Network updatedNetwork = dockerClient.inspectNetworkCmd().withNetworkId(network.getId()).exec(); Network.ContainerNetworkConfig containerNetworkConfig = updatedNetwork.getContainers().get(container.getId()); assertNotNull(containerNetworkConfig); - assertEquals(containerNetworkConfig.getIpv4Address(), "10.100.100.100/24"); + assertThat(containerNetworkConfig.getIpv4Address(), is(CONTAINER_IP + "/24")); InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); - ContainerNetwork testNetwork = inspectContainerResponse.getNetworkSettings().getNetworks().get("testNetwork"); + ContainerNetwork testNetwork = inspectContainerResponse.getNetworkSettings().getNetworks().get(NETWORK_NAME); assertNotNull(testNetwork); - assertEquals(testNetwork.getAliases(), Collections.singletonList("testing")); - assertEquals(testNetwork.getGateway(), "10.100.100.1"); - assertEquals(testNetwork.getIpAddress(), "10.100.100.100"); + assertThat(testNetwork.getAliases(), hasItem("aliasName")); + assertEquals(testNetwork.getGateway(), "10.100.102.1"); + assertEquals(testNetwork.getIpAddress(), CONTAINER_IP); } } diff --git a/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java index 62c7b2ec0..e51db21f4 100644 --- a/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/ContainerDiffCmdImplTest.java @@ -67,13 +67,9 @@ public void testContainerDiff() throws DockerException { assertThat(testChangeLog, hasField("kind", equalTo(1))); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void testContainerDiffWithNonExistingContainer() throws DockerException { - try { - dockerClient.containerDiffCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } - } + dockerClient.containerDiffCmd("non-existing").exec(); + } } diff --git a/src/test/java/com/github/dockerjava/core/command/CopyArchiveFromContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/CopyArchiveFromContainerCmdImplTest.java index 9b30dd891..59c459382 100644 --- a/src/test/java/com/github/dockerjava/core/command/CopyArchiveFromContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/CopyArchiveFromContainerCmdImplTest.java @@ -70,13 +70,10 @@ public void copyFromContainer() throws Exception { assertTrue(responseAsString.length() > 0); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void copyFromNonExistingContainer() throws Exception { - try { - dockerClient.copyArchiveFromContainerCmd("non-existing", "/test").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException ignored) { - } + + dockerClient.copyArchiveFromContainerCmd("non-existing", "/test").exec(); } @Test diff --git a/src/test/java/com/github/dockerjava/core/command/CopyArchiveToContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/CopyArchiveToContainerCmdImplTest.java index a1006277c..658c1f090 100644 --- a/src/test/java/com/github/dockerjava/core/command/CopyArchiveToContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/CopyArchiveToContainerCmdImplTest.java @@ -83,14 +83,10 @@ private void assertFileCopied(CreateContainerResponse container) throws IOExcept } } - @Test + @Test(expectedExceptions = NotFoundException.class) public void copyToNonExistingContainer() throws Exception { - try { - dockerClient.copyArchiveToContainerCmd("non-existing").withHostResource("src/test/resources/testReadFile") - .exec(); - fail("expected NotFoundException"); - } catch (NotFoundException ignored) { - } + + dockerClient.copyArchiveToContainerCmd("non-existing").withHostResource("src/test/resources/testReadFile").exec(); } @Test diff --git a/src/test/java/com/github/dockerjava/core/command/CopyFileFromContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/CopyFileFromContainerCmdImplTest.java index 3e4e88612..ec66287eb 100644 --- a/src/test/java/com/github/dockerjava/core/command/CopyFileFromContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/CopyFileFromContainerCmdImplTest.java @@ -1,5 +1,6 @@ package com.github.dockerjava.core.command; +import static com.github.dockerjava.utils.TestUtils.getVersion; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.not; @@ -7,7 +8,10 @@ import java.io.InputStream; import java.lang.reflect.Method; +import com.github.dockerjava.core.RemoteApiVersion; +import com.github.dockerjava.utils.TestUtils; import org.testng.ITestResult; +import org.testng.SkipException; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeMethod; @@ -43,6 +47,10 @@ public void afterMethod(ITestResult result) { @Test public void copyFromContainer() throws Exception { + if (getVersion(dockerClient).isGreaterOrEqual(RemoteApiVersion.VERSION_1_24)) { + throw new SkipException("Doesn't work since 1.24"); + } + // TODO extract this into a shared method CreateContainerResponse container = dockerClient.createContainerCmd("busybox") .withName("docker-java-itest-copyFromContainer").withCmd("touch", "/copyFromContainer").exec(); @@ -62,12 +70,9 @@ public void copyFromContainer() throws Exception { assertTrue(responseAsString.length() > 0); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void copyFromNonExistingContainer() throws Exception { - try { - dockerClient.copyFileFromContainerCmd("non-existing", "/test").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException ignored) { - } + + dockerClient.copyFileFromContainerCmd("non-existing", "/test").exec(); } } diff --git a/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java index a93e100c6..1f62527e9 100644 --- a/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java @@ -10,6 +10,7 @@ import com.github.dockerjava.api.model.Device; import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.Frame; +import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Link; import com.github.dockerjava.api.model.LogConfig; import com.github.dockerjava.api.model.Network; @@ -21,6 +22,7 @@ import com.github.dockerjava.api.model.Ports.Binding; import com.github.dockerjava.client.AbstractDockerClientTest; +import org.apache.commons.io.FileUtils; import org.testng.ITestResult; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; @@ -45,6 +47,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItemInArray; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isEmptyString; @@ -191,9 +194,12 @@ public void createContainerWithVolumesFrom() throws DockerException { @Test public void createContainerWithEnv() throws Exception { + final String testVariable = "VARIABLE=success"; - CreateContainerResponse container = dockerClient.createContainerCmd(BUSYBOX_IMAGE).withEnv("VARIABLE=success") - .withCmd("env").exec(); + CreateContainerResponse container = dockerClient.createContainerCmd(BUSYBOX_IMAGE) + .withEnv(testVariable) + .withCmd("env") + .exec(); LOG.info("Created container {}", container.toString()); @@ -201,11 +207,11 @@ public void createContainerWithEnv() throws Exception { InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); - assertThat(Arrays.asList(inspectContainerResponse.getConfig().getEnv()), containsInAnyOrder("VARIABLE=success")); + assertThat(Arrays.asList(inspectContainerResponse.getConfig().getEnv()), hasItem(testVariable)); dockerClient.startContainerCmd(container.getId()).exec(); - assertThat(containerLog(container.getId()), containsString("VARIABLE=success")); + assertThat(containerLog(container.getId()), containsString(testVariable)); } @Test @@ -227,7 +233,7 @@ public void createContainerWithHostname() throws Exception { assertThat(containerLog(container.getId()), containsString("HOSTNAME=docker-java")); } - @Test + @Test(expectedExceptions = ConflictException.class) public void createContainerWithName() throws DockerException { CreateContainerResponse container = dockerClient.createContainerCmd(BUSYBOX_IMAGE).withName("container") @@ -241,12 +247,8 @@ public void createContainerWithName() throws DockerException { assertThat(inspectContainerResponse.getName(), equalTo("/container")); - try { - dockerClient.createContainerCmd(BUSYBOX_IMAGE).withName("container").withCmd("env").exec(); - fail("Expected ConflictException"); - } catch (ConflictException e) { - } + dockerClient.createContainerCmd(BUSYBOX_IMAGE).withName("container").withCmd("env").exec(); } @Test @@ -373,7 +375,7 @@ public void createContainerWithAlias() throws DockerException { .exec(); ContainerNetwork aliasNet = inspectContainerResponse.getNetworkSettings().getNetworks().get("aliasNet"); - assertEquals(aliasNet.getAliases(), Collections.singletonList("server")); + assertThat(aliasNet.getAliases(), hasItem("server")); } @Test @@ -735,4 +737,20 @@ public void createContainerWithCgroupParent() throws DockerException { assertThat(inspectContainer.getHostConfig().getCgroupParent(), is("/parent")); } + + @SuppressWarnings("Duplicates") + @Test + public void createContainerWithShmSize() throws DockerException { + HostConfig hostConfig = new HostConfig().withShmSize(96 * FileUtils.ONE_MB); + CreateContainerResponse container = dockerClient.createContainerCmd(BUSYBOX_IMAGE) + .withHostConfig(hostConfig).withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertEquals(inspectContainerResponse.getHostConfig().getShmSize(), hostConfig.getShmSize()); + } } diff --git a/src/test/java/com/github/dockerjava/core/command/EventsCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/EventsCmdImplTest.java index 939f95087..d05327a06 100644 --- a/src/test/java/com/github/dockerjava/core/command/EventsCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/EventsCmdImplTest.java @@ -17,11 +17,14 @@ import com.github.dockerjava.api.model.Event; import com.github.dockerjava.client.AbstractDockerClientTest; +/* + * NOTE: These tests may fail if there is a difference between local and daemon time + * (this is especially a problem when using boot2docker as time may not in sync + * with the virtualbox host system) + */ @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); } @@ -46,9 +49,6 @@ public void afterMethod(ITestResult result) { super.afterMethod(result); } - /* - * This specific test may fail with boot2docker as time may not in sync with host system - */ @Test public void testEventStreamTimeBound() throws Exception { // Don't include other tests events @@ -58,82 +58,84 @@ public void testEventStreamTimeBound() throws Exception { int expectedEvents = generateEvents(); String endTime = getEpochTime(); - CountDownLatch countDownLatch = new CountDownLatch(expectedEvents); - EventsTestCallback eventCallback = new EventsTestCallback(countDownLatch); - - dockerClient.eventsCmd().withSince(startTime).withUntil(endTime).exec(eventCallback); + EventsTestCallback eventCallback = new EventsTestCallback(expectedEvents); - Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); + dockerClient.eventsCmd() + .withSince(startTime) + .withUntil(endTime) + .exec(eventCallback); - eventCallback.close(); + List events = eventCallback.awaitExpectedEvents(3, TimeUnit.MINUTES); - assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); + // we may receive more events as expected + assertTrue(events.size() >= expectedEvents, "Received events: " + events); } @Test - public void testEventStreaming1() throws Exception { - // Don't include other tests events - TimeUnit.SECONDS.sleep(1); - - CountDownLatch countDownLatch = new CountDownLatch(KNOWN_NUM_EVENTS); - EventsTestCallback eventCallback = new EventsTestCallback(countDownLatch); - - dockerClient.eventsCmd().withSince(getEpochTime()).exec(eventCallback); - - generateEvents(); - - Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); - - eventCallback.close(); - assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); - } - - @Test - public void testEventStreaming2() throws Exception { + public void testEventStreaming() throws Exception { // Don't include other tests events TimeUnit.SECONDS.sleep(1); + + String startTime = getEpochTime(); + int expectedEvents = generateEvents(); - CountDownLatch countDownLatch = new CountDownLatch(KNOWN_NUM_EVENTS); - EventsTestCallback eventCallback = new EventsTestCallback(countDownLatch); + EventsTestCallback eventCallback = new EventsTestCallback(expectedEvents); - dockerClient.eventsCmd().withSince(getEpochTime()).exec(eventCallback); + dockerClient.eventsCmd() + .withSince(startTime) + .exec(eventCallback); generateEvents(); - Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); - - eventCallback.close(); - assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); + List events = eventCallback.awaitExpectedEvents(3, TimeUnit.MINUTES); + + // we may receive more events as expected + assertTrue(events.size() >= expectedEvents, "Received events: " + events); } + public void testEventStreamingWithFilter() throws Exception { // Don't include other tests events TimeUnit.SECONDS.sleep(1); + + String startTime = getEpochTime(); + int expectedEvents = 1; - CountDownLatch countDownLatch = new CountDownLatch(1); - EventsTestCallback eventCallback = dockerClient.eventsCmd().withEventFilter("start") - .exec(new EventsTestCallback(countDownLatch)); + EventsTestCallback eventCallback = new EventsTestCallback(expectedEvents); + + dockerClient.eventsCmd() + .withSince(startTime) + .withEventFilter("start") + .exec(eventCallback); generateEvents(); - Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); - - eventCallback.close(); - assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); + List events = eventCallback.awaitExpectedEvents(3, TimeUnit.MINUTES); + + // we should get exactly one "start" event here + assertEquals(events.size(), expectedEvents, "Received events: " + events); } /** - * This method generates {#link KNOWN_NUM_EVENTS} events + * This method generates some events and returns the number of events being generated */ private int generateEvents() throws Exception { - String testImage = "busybox"; + String testImage = "busybox:latest"; dockerClient.pullImageCmd(testImage).exec(new PullImageResultCallback()).awaitSuccess(); - CreateContainerResponse container = dockerClient.createContainerCmd(testImage).withCmd("sleep", "9999").exec(); dockerClient.startContainerCmd(container.getId()).exec(); - dockerClient.stopContainerCmd(container.getId()).exec(); - return KNOWN_NUM_EVENTS; + dockerClient.stopContainerCmd(container.getId()).withTimeout(1).exec(); + + // generates 5 events with remote api 1.24: + + // Event[status=pull,id=busybox:latest,from=,node=,type=IMAGE,action=pull,actor=com.github.dockerjava.api.model.EventActor@417db6d7[id=busybox:latest,attributes={name=busybox}],time=1473455186,timeNano=1473455186436681587] + // Event[status=create,id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,from=busybox:latest,node=,type=CONTAINER,action=create,actor=com.github.dockerjava.api.model.EventActor@40bcec[id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,attributes={image=busybox:latest, name=sick_lamport}],time=1473455186,timeNano=1473455186470713257] + // Event[status=,id=,from=,node=,type=NETWORK,action=connect,actor=com.github.dockerjava.api.model.EventActor@318a1b01[id=10870ceb13abb7cf841ea68868472da881b33c8ed08d2cde7dbb39d7c24d1d27,attributes={container=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c, name=bridge, type=bridge}],time=1473455186,timeNano=1473455186544318466] + // Event[status=start,id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,from=busybox:latest,node=,type=CONTAINER,action=start,actor=com.github.dockerjava.api.model.EventActor@606f43a3[id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,attributes={image=busybox:latest, name=sick_lamport}],time=1473455186,timeNano=1473455186786163819] + // Event[status=kill,id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,from=busybox:latest,node=,type=CONTAINER,action=kill,actor=com.github.dockerjava.api.model.EventActor@72a9ffcf[id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,attributes={image=busybox:latest, name=sick_lamport, signal=15}],time=1473455186,timeNano=1473455186792963392] + + return 5; } private class EventsTestCallback extends EventsResultCallback { @@ -142,18 +144,24 @@ private class EventsTestCallback extends EventsResultCallback { private final List events = new ArrayList(); - public EventsTestCallback(CountDownLatch countDownLatch) { - this.countDownLatch = countDownLatch; + public EventsTestCallback(int expextedEvents) { + this.countDownLatch = new CountDownLatch(expextedEvents); } public void onNext(Event event) { LOG.info("Received event #{}: {}", countDownLatch.getCount(), event); - countDownLatch.countDown(); events.add(event); + countDownLatch.countDown(); } - - public List getEvents() { - return new ArrayList(events); + + public List awaitExpectedEvents(long timeout, TimeUnit unit ) { + try { + countDownLatch.await(timeout, unit); + close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + 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 index 139c81f56..d8e3326d5 100644 --- a/src/test/java/com/github/dockerjava/core/command/ExecCreateCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/ExecCreateCmdImplTest.java @@ -44,7 +44,7 @@ public void afterMethod(ITestResult result) { public void execCreateTest() { String containerName = "generated_" + new SecureRandom().nextInt(); - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("top") + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withUser("root").withCmd("top") .withName(containerName).exec(); LOG.info("Created container {}", container.toString()); diff --git a/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java index c4f0b9189..8ae9e5a91 100644 --- a/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/ExecStartCmdImplTest.java @@ -1,13 +1,15 @@ package com.github.dockerjava.core.command; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.isEmptyString; -import static org.hamcrest.Matchers.not; - +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.client.AbstractDockerClientTest; 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; import org.testng.ITestResult; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; @@ -15,10 +17,6 @@ import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; -import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.api.command.ExecCreateCmdResponse; -import com.github.dockerjava.client.AbstractDockerClientTest; - @Test(groups = "integration") public class ExecStartCmdImplTest extends AbstractDockerClientTest { @BeforeTest @@ -53,7 +51,7 @@ public void execStart() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) - .withAttachStdout(true).withCmd("touch", "/execStartTest.log").exec(); + .withAttachStdout(true).withCmd("touch", "/execStartTest.log").withUser("root").exec(); dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec( new ExecStartResultCallback(System.out, System.err)).awaitCompletion(); @@ -92,4 +90,23 @@ public void execStartAttached() throws Exception { assertNotNull(responseAsString); assertTrue(responseAsString.length() > 0); } + + @Test(groups = "ignoreInCircleCi", expectedExceptions = NotFoundException.class) + public void execStartWithNonExistentUser() throws Exception { + String containerName = "generated_" + new SecureRandom().nextInt(); + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .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").withUser("NonExistentUser").exec(); + dockerClient.execStartCmd(execCreateCmdResponse.getId()).withDetach(false).withTty(true) + .exec(new ExecStartResultCallback(System.out, System.err)).awaitCompletion(); + + dockerClient.copyArchiveFromContainerCmd(container.getId(), "/execStartTest.log").exec(); + } } diff --git a/src/test/java/com/github/dockerjava/core/command/KillContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/KillContainerCmdImplTest.java index 3ba85928e..c8589271e 100644 --- a/src/test/java/com/github/dockerjava/core/command/KillContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/KillContainerCmdImplTest.java @@ -67,14 +67,10 @@ public void killContainer() throws DockerException { } - @Test + @Test(expectedExceptions = NotFoundException.class) public void killNonExistingContainer() throws DockerException { - try { - dockerClient.killContainerCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + dockerClient.killContainerCmd("non-existing").exec(); } } diff --git a/src/test/java/com/github/dockerjava/core/command/LoadImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/LoadImageCmdImplTest.java new file mode 100644 index 000000000..7cecf7334 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/LoadImageCmdImplTest.java @@ -0,0 +1,76 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.model.Image; +import com.github.dockerjava.client.AbstractDockerClientTest; + +import com.github.dockerjava.utils.TestResources; +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.InputStream; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.hamcrest.core.IsEqual.equalTo; + +@Test(groups = "integration") +public class LoadImageCmdImplTest extends AbstractDockerClientTest { + + private String expectedImageId; + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + expectedImageId = "sha256:56031f66eb0cef2e2e5cb2d1dabafaa0ebcd0a18a507d313b5bdb8c0472c5eba"; + if (findImageWithId(expectedImageId, dockerClient.listImagesCmd().exec()) != null) { + dockerClient.removeImageCmd(expectedImageId).exec(); + } + } + + @AfterMethod + public void afterMethod(ITestResult result) { + dockerClient.removeImageCmd(expectedImageId).exec(); + super.afterMethod(result); + } + + @Test + public void loadImageFromTar() throws Exception { + try (InputStream uploadStream = Files.newInputStream(TestResources.getApiImagesLoadTestTarball())) { + dockerClient.loadImageCmd(uploadStream).exec(); + } + + final Image image = findImageWithId(expectedImageId, dockerClient.listImagesCmd().exec()); + + assertThat("Can't find expected image after loading from a tar archive!", image, notNullValue()); + assertThat("Image after loading from a tar archive has wrong tags!", + asList(image.getRepoTags()), equalTo(singletonList("docker-java/load:1.0"))); + } + + private Image findImageWithId(final String id, final List images) { + for (Image image : images) { + if (id.equals(image.getId())) { + return image; + } + } + return null; + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java index 817179eb2..869bcbb1a 100644 --- a/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; import org.testng.ITestResult; import org.testng.annotations.AfterMethod; @@ -17,6 +18,7 @@ import org.testng.annotations.Test; import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.model.StreamType; import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.client.AbstractDockerClientTest; @@ -44,31 +46,65 @@ public void afterMethod(ITestResult result) { } @Test - public void asyncLogContainer() throws Exception { + public void asyncLogContainerWithTtyEnabled() throws Exception { - String snippet = "hello world"; - - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/echo", snippet) + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("/bin/sh", "-c", "while true; do echo hello; sleep 1; done") + .withTty(true) .exec(); LOG.info("Created container: {}", container.toString()); assertThat(container.getId(), not(isEmptyString())); - dockerClient.startContainerCmd(container.getId()).exec(); + dockerClient.startContainerCmd(container.getId()) + .exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) - .awaitStatusCode(); + LogContainerTestCallback loggingCallback = new LogContainerTestCallback(true); - assertThat(exitCode, equalTo(0)); + // this essentially test the since=0 case + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .withFollowStream(true) + .withTailAll() + .exec(loggingCallback); - LogContainerTestCallback loggingCallback = new LogContainerTestCallback(); + loggingCallback.awaitCompletion(3, TimeUnit.SECONDS); + + assertTrue(loggingCallback.toString().contains("hello")); + + assertEquals(loggingCallback.getCollectedFrames().get(0).getStreamType(), StreamType.RAW); + } + + @Test + public void asyncLogContainerWithTtyDisabled() throws Exception { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("/bin/sh", "-c", "while true; do echo hello; sleep 1; done") + .withTty(false) + .exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()) + .exec(); + + LogContainerTestCallback loggingCallback = new LogContainerTestCallback(true); // this essentially test the since=0 case - dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .withFollowStream(true) + .withTailAll() + .exec(loggingCallback); - loggingCallback.awaitCompletion(); + loggingCallback.awaitCompletion(3, TimeUnit.SECONDS); - assertTrue(loggingCallback.toString().contains(snippet)); + assertTrue(loggingCallback.toString().contains("hello")); + + assertEquals(loggingCallback.getCollectedFrames().get(0).getStreamType(), StreamType.STDOUT); } @Test @@ -105,7 +141,8 @@ public void asyncMultipleLogContainer() throws Exception { String snippet = "hello world"; - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/echo", snippet) + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("/bin/echo", snippet) .exec(); LOG.info("Created container: {}", container.toString()); @@ -113,26 +150,36 @@ public void asyncMultipleLogContainer() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + int exitCode = dockerClient.waitContainerCmd(container.getId()) + .exec(new WaitContainerResultCallback()) .awaitStatusCode(); assertThat(exitCode, equalTo(0)); LogContainerTestCallback loggingCallback = new LogContainerTestCallback(); - dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .exec(loggingCallback); loggingCallback.close(); loggingCallback = new LogContainerTestCallback(); - dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .exec(loggingCallback); loggingCallback.close(); loggingCallback = new LogContainerTestCallback(); - dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .exec(loggingCallback); loggingCallback.awaitCompletion(); @@ -143,7 +190,8 @@ public void asyncMultipleLogContainer() throws Exception { public void asyncLogContainerWithSince() throws Exception { String snippet = "hello world"; - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/echo", snippet) + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("/bin/echo", snippet) .exec(); LOG.info("Created container: {}", container.toString()); @@ -153,19 +201,22 @@ public void asyncLogContainerWithSince() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + int exitCode = dockerClient.waitContainerCmd(container.getId()) + .exec(new WaitContainerResultCallback()) .awaitStatusCode(); assertThat(exitCode, equalTo(0)); LogContainerTestCallback loggingCallback = new LogContainerTestCallback(); - dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withSince(timestamp) + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .withSince(timestamp) .exec(loggingCallback); loggingCallback.awaitCompletion(); assertThat(loggingCallback.toString(), containsString(snippet)); } - } diff --git a/src/test/java/com/github/dockerjava/core/command/PauseCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/PauseCmdImplTest.java new file mode 100644 index 000000000..f100c4f89 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/PauseCmdImplTest.java @@ -0,0 +1,103 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.exception.InternalServerErrorException; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.client.AbstractDockerClientTest; +import com.github.dockerjava.utils.ContainerUtils; +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.lang.reflect.Method; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +@Test(groups = {"integration", "ignoreInCircleCi"}) +public class PauseCmdImplTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(PauseCmdImplTest.class); + + @BeforeTest + public void beforeTest() throws Exception { + 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 pauseRunningContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.pauseContainer(dockerClient, container); + } + + @Test(expectedExceptions = NotFoundException.class) + public void pauseNonExistingContainer() { + + dockerClient.pauseContainerCmd("non-existing").exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void pauseStoppedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.stopContainer(dockerClient, container); + + dockerClient.pauseContainerCmd(container.getId()).exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void pausePausedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.pauseContainer(dockerClient, container); + + dockerClient.pauseContainerCmd(container.getId()).exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void pauseCreatedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.pauseContainerCmd(container.getId()).exec(); + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/PushImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/PushImageCmdImplTest.java index e89a071b8..0fb32c311 100644 --- a/src/test/java/com/github/dockerjava/core/command/PushImageCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/PushImageCmdImplTest.java @@ -19,7 +19,7 @@ import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.client.AbstractDockerClientTest; -@Test(groups = "integration") +@Test(groups = {"integration-auth", "integration"}) public class PushImageCmdImplTest extends AbstractDockerClientTest { public static final Logger LOG = LoggerFactory.getLogger(PushImageCmdImplTest.class); diff --git a/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java index 87ace71df..26c8e0e19 100644 --- a/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/RemoveContainerCmdImplTest.java @@ -68,13 +68,10 @@ public void removeContainer() throws DockerException { } - @Test + @Test(expectedExceptions = NotFoundException.class) public void removeNonExistingContainer() throws DockerException { - try { - dockerClient.removeContainerCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + + dockerClient.removeContainerCmd("non-existing").exec(); } } diff --git a/src/test/java/com/github/dockerjava/core/command/RemoveImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/RemoveImageCmdImplTest.java index 90be81c7f..4427da997 100644 --- a/src/test/java/com/github/dockerjava/core/command/RemoveImageCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/RemoveImageCmdImplTest.java @@ -74,14 +74,10 @@ public void removeImage() throws DockerException, InterruptedException { assertThat(containers, matcher); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void removeNonExistingImage() throws DockerException, InterruptedException { - try { - dockerClient.removeImageCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + dockerClient.removeImageCmd("non-existing").exec(); } } diff --git a/src/test/java/com/github/dockerjava/core/command/RemoveNetworkCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/RemoveNetworkCmdImplTest.java index e1e4e45fe..228e48c4d 100644 --- a/src/test/java/com/github/dockerjava/core/command/RemoveNetworkCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/RemoveNetworkCmdImplTest.java @@ -64,13 +64,10 @@ public void removeNetwork() throws DockerException { } - @Test + @Test(expectedExceptions = NotFoundException.class) public void removeNonExistingContainer() throws DockerException { - try { - dockerClient.removeNetworkCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + + dockerClient.removeNetworkCmd("non-existing").exec(); } } diff --git a/src/test/java/com/github/dockerjava/core/command/RemoveVolumeCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/RemoveVolumeCmdImplTest.java index 249bf8493..9ed690b65 100644 --- a/src/test/java/com/github/dockerjava/core/command/RemoveVolumeCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/RemoveVolumeCmdImplTest.java @@ -41,7 +41,7 @@ public void afterMethod(ITestResult result) { super.afterMethod(result); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void removeVolume() throws DockerException { String volumeName = "volume1"; @@ -55,11 +55,6 @@ public void removeVolume() throws DockerException { dockerClient.removeVolumeCmd(volumeName).exec(); - try { - dockerClient.inspectVolumeCmd(volumeName).exec(); - fail("Expected NotFoundException"); - } catch (NotFoundException e) { - // just ignore - } + dockerClient.inspectVolumeCmd(volumeName).exec(); } } diff --git a/src/test/java/com/github/dockerjava/core/command/RestartContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/RestartContainerCmdImplTest.java index 4920ac087..84916f082 100644 --- a/src/test/java/com/github/dockerjava/core/command/RestartContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/RestartContainerCmdImplTest.java @@ -71,14 +71,9 @@ public void restartContainer() throws DockerException { dockerClient.killContainerCmd(container.getId()).exec(); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void restartNonExistingContainer() throws DockerException, InterruptedException { - try { - dockerClient.restartContainerCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + dockerClient.restartContainerCmd("non-existing").exec(); } - } diff --git a/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java index 2b32e00c5..4bb8f2b99 100644 --- a/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/StartContainerCmdImplTest.java @@ -251,7 +251,7 @@ public void startContainerWithRandomPortBindings() throws DockerException { } - @Test + @Test(expectedExceptions = InternalServerErrorException.class) public void startContainerWithConflictingPortBindings() throws DockerException { ExposedPort tcp22 = ExposedPort.tcp(22); @@ -268,13 +268,7 @@ public void startContainerWithConflictingPortBindings() throws DockerException { assertThat(container.getId(), not(isEmptyString())); - try { - dockerClient.startContainerCmd(container.getId()).exec(); - fail("expected InternalServerErrorException"); - } catch (InternalServerErrorException e) { - - } - + dockerClient.startContainerCmd(container.getId()).exec(); } @Test @@ -412,13 +406,10 @@ public void startContainer() throws DockerException { } } - @Test + @Test(expectedExceptions = NotFoundException.class) public void testStartNonExistingContainer() throws DockerException { - try { + dockerClient.startContainerCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } } /** diff --git a/src/test/java/com/github/dockerjava/core/command/StopContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/StopContainerCmdImplTest.java index 913ed9474..7ae6a09cc 100644 --- a/src/test/java/com/github/dockerjava/core/command/StopContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/StopContainerCmdImplTest.java @@ -69,21 +69,15 @@ public void testStopContainer() throws DockerException { assertThat(inspectContainerResponse.getState().getRunning(), is(equalTo(false))); final Integer exitCode = inspectContainerResponse.getState().getExitCode(); - if (apiVersion.equals(VERSION_1_22)) { - assertThat(exitCode, is(0)); - } else { - assertThat(exitCode, not(0)); - } + + assertThat(exitCode, is(137)); + } - @Test + @Test(expectedExceptions = NotFoundException.class) public void testStopNonExistingContainer() throws DockerException { - try { - dockerClient.stopContainerCmd("non-existing").withTimeout(2).exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + dockerClient.stopContainerCmd("non-existing").withTimeout(2).exec(); } } diff --git a/src/test/java/com/github/dockerjava/core/command/TagImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/TagImageCmdImplTest.java index 94d4291d0..3bf033212 100644 --- a/src/test/java/com/github/dockerjava/core/command/TagImageCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/TagImageCmdImplTest.java @@ -49,15 +49,11 @@ public void tagImage() throws Exception { dockerClient.removeImageCmd("docker-java/busybox:" + tag).exec(); } - @Test + @Test(expectedExceptions = NotFoundException.class) 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) { - } + String tag = "" + RandomUtils.nextInt(Integer.MAX_VALUE); + dockerClient.tagImageCmd("non-existing", "docker-java/busybox", tag).exec(); } } diff --git a/src/test/java/com/github/dockerjava/core/command/UnpauseCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/UnpauseCmdImplTest.java new file mode 100644 index 000000000..2f41b67ae --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/UnpauseCmdImplTest.java @@ -0,0 +1,118 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.exception.InternalServerErrorException; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.client.AbstractDockerClientTest; +import com.github.dockerjava.utils.ContainerUtils; +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.lang.reflect.Method; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +@Test(groups = {"integration", "ignoreInCircleCi"}) +public class UnpauseCmdImplTest extends AbstractDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(UnpauseCmdImplTest.class); + + @BeforeTest + public void beforeTest() throws Exception { + 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 unpausePausedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.pauseContainer(dockerClient, container); + + ContainerUtils.unpauseContainer(dockerClient, container); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void unpauseRunningContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + dockerClient.unpauseContainerCmd(container.getId()).exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void unpauseStoppedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.stopContainer(dockerClient, container); + + dockerClient.unpauseContainerCmd(container.getId()).exec(); + } + + @Test(expectedExceptions = NotFoundException.class) + public void unpauseNonExistingContainer() { + + dockerClient.unpauseContainerCmd("non-existing").exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void unpauseCreatedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.unpauseContainerCmd(container.getId()).exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void unpauseUnpausedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.pauseContainer(dockerClient, container); + + dockerClient.unpauseContainerCmd(container.getId()).exec(); + dockerClient.unpauseContainerCmd(container.getId()).exec(); + } +} diff --git a/src/test/java/com/github/dockerjava/core/command/UpdateContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/UpdateContainerCmdImplTest.java index c230fbb02..df53d671b 100644 --- a/src/test/java/com/github/dockerjava/core/command/UpdateContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/UpdateContainerCmdImplTest.java @@ -79,15 +79,15 @@ public void updateContainer() throws DockerException, IOException { // .withCpusetCpus("0") // depends on env .withCpusetMems("0") .withMemory(314572800L) - .withMemorySwap(514288000L) +// .withMemorySwap(514288000L) Your kernel does not support swap limit capabilities, memory limited without swap. .withMemoryReservation(209715200L) // .withKernelMemory(52428800) Can not update kernel memory to a running container, please stop it first. .exec(); - // docker toolbox 1.10.1 - assertThat(updateResponse.getWarnings(), hasSize(1)); - assertThat(updateResponse.getWarnings().get(0), - is("Your kernel does not support Block I/O weight. Weight discarded.")); + // true only on docker toolbox (1.10.1) +// assertThat(updateResponse.getWarnings(), hasSize(1)); +// assertThat(updateResponse.getWarnings().get(0), +// is("Your kernel does not support Block I/O weight. Weight discarded.")); InspectContainerResponse inspectAfter = dockerClient.inspectContainerCmd(containerId).exec(); final HostConfig afterHostConfig = inspectAfter.getHostConfig(); @@ -102,7 +102,7 @@ public void updateContainer() throws DockerException, IOException { assertThat(afterHostConfig.getCpusetMems(), is("0")); assertThat(afterHostConfig.getMemoryReservation(), is(209715200L)); - assertThat(afterHostConfig.getMemorySwap(), is(514288000L)); +// assertThat(afterHostConfig.getMemorySwap(), is(514288000L)); } diff --git a/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileAddMultipleFilesTest.java b/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileAddMultipleFilesTest.java index 8997a6d80..b37754a6a 100644 --- a/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileAddMultipleFilesTest.java +++ b/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileAddMultipleFilesTest.java @@ -1,20 +1,20 @@ package com.github.dockerjava.core.dockerfile; -import com.google.common.base.Function; -import junit.framework.TestCase; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.Test; +import static com.google.common.collect.Collections2.transform; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; import java.io.File; import java.io.IOException; import java.util.Collection; -import static com.google.common.collect.Collections2.transform; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import com.google.common.base.Function; -public class DockerfileAddMultipleFilesTest extends TestCase { +public class DockerfileAddMultipleFilesTest { private static final Logger log = LoggerFactory.getLogger(DockerfileAddMultipleFilesTest.class); @@ -26,13 +26,48 @@ public String apply(File file) { }; @Test - public void testAddMultipleFiles() throws IOException { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddMultipleFiles") - .getFile()); + public void nestedDirsPatterns() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/NestedDirsDockerignore"); + Dockerfile dockerfile = new Dockerfile(new File(baseDir, "Dockerfile"), baseDir); + Dockerfile.ScannedResult result = dockerfile.parse(); + Collection filesToAdd = transform(result.filesToAdd, TO_FILE_NAMES); + + assertThat(filesToAdd, + containsInAnyOrder("Dockerfile", ".dockerignore", "README.md", "README-grand.md", "b.txt")); + } + + @Test + public void effectiveIgnorePatterns() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/EffectiveDockerignorePatterns"); + Dockerfile dockerfile = new Dockerfile(new File(baseDir, "Dockerfile"), baseDir); + Dockerfile.ScannedResult result = dockerfile.parse(); + Collection filesToAdd = transform(result.filesToAdd, TO_FILE_NAMES); + + assertThat(filesToAdd, containsInAnyOrder("Dockerfile", ".dockerignore", "README.md")); + } + + @Test + public void ineffectiveIgnorePattern() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/IneffectiveDockerignorePattern"); + Dockerfile dockerfile = new Dockerfile(new File(baseDir, "Dockerfile"), baseDir); + Dockerfile.ScannedResult result = dockerfile.parse(); + Collection filesToAdd = transform(result.filesToAdd, TO_FILE_NAMES); + + assertThat(filesToAdd, containsInAnyOrder("Dockerfile", ".dockerignore", "README.md", "README-secret.md")); + } + + @Test + public void addFiles() throws IOException { + File baseDir = fileFromBuildTestResource("ADD/files"); Dockerfile dockerfile = new Dockerfile(new File(baseDir, "Dockerfile"), baseDir); Dockerfile.ScannedResult result = dockerfile.parse(); Collection filesToAdd = transform(result.filesToAdd, TO_FILE_NAMES); assertThat(filesToAdd, containsInAnyOrder("Dockerfile", "src1", "src2")); } + + private File fileFromBuildTestResource(String resource) { + return new File(Thread.currentThread().getContextClassLoader() + .getResource("buildTests/" + resource).getFile()); + } } diff --git a/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileTest.java b/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileTest.java deleted file mode 100644 index 390bcf647..000000000 --- a/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.github.dockerjava.core.dockerfile; - -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import junit.framework.TestCase; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.Test; - -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"), baseDir); - 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)); - } - } - -} diff --git a/src/test/java/com/github/dockerjava/netty/AbstractNettyDockerClientTest.java b/src/test/java/com/github/dockerjava/netty/AbstractNettyDockerClientTest.java index 44a541808..57f7aa421 100644 --- a/src/test/java/com/github/dockerjava/netty/AbstractNettyDockerClientTest.java +++ b/src/test/java/com/github/dockerjava/netty/AbstractNettyDockerClientTest.java @@ -12,6 +12,6 @@ public abstract class AbstractNettyDockerClientTest extends AbstractDockerClient @Override protected TestDockerCmdExecFactory initTestDockerCmdExecFactory() { - return new TestDockerCmdExecFactory(new DockerCmdExecFactoryImpl()); + return new TestDockerCmdExecFactory(new NettyDockerCmdExecFactory()); } } diff --git a/src/test/java/com/github/dockerjava/netty/WebTargetTest.java b/src/test/java/com/github/dockerjava/netty/WebTargetTest.java new file mode 100644 index 000000000..a462eb8b5 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/WebTargetTest.java @@ -0,0 +1,39 @@ +package com.github.dockerjava.netty; + +import static org.testng.Assert.assertEquals; + +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Alexander Koshevoy + */ +public class WebTargetTest { + @Mock private ChannelProvider channelProvider; + + @BeforeMethod + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void verifyImmutability() throws Exception { + WebTarget emptyWebTarget = new WebTarget(channelProvider); + + WebTarget initWebTarget = emptyWebTarget.path("/containers/{id}/attach").resolveTemplate("id", "d03da378b592") + .queryParam("logs", "true"); + + WebTarget anotherWebTarget = emptyWebTarget.path("/containers/{id}/attach") + .resolveTemplate("id", "2cfada4e3c07").queryParam("stdin", "true"); + + assertEquals(new WebTarget(channelProvider), emptyWebTarget); + + assertEquals(new WebTarget(channelProvider).path("/containers/d03da378b592/attach") + .queryParam("logs", "true"), initWebTarget); + + assertEquals(new WebTarget(channelProvider).path("/containers/2cfada4e3c07/attach") + .queryParam("stdin", "true"), anotherWebTarget); + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java index 7f8e66609..66219dc72 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/AttachContainerCmdExecTest.java @@ -1,15 +1,15 @@ package com.github.dockerjava.netty.exec; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.not; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import java.lang.reflect.Method; -import java.util.concurrent.TimeUnit; import org.testng.ITestResult; import org.testng.annotations.AfterMethod; @@ -70,7 +70,7 @@ public void onNext(Frame frame) { }; dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true) - .withLogs(true).exec(callback).awaitCompletion(10, TimeUnit.SECONDS); + .withLogs(true).exec(callback).awaitCompletion(10, SECONDS); callback.close(); assertThat(callback.toString(), containsString(snippet)); @@ -81,8 +81,11 @@ public void attachContainerWithStdin() throws Exception { String snippet = "hello world"; - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/sh", "-c", "read line && echo $line") - .withTty(false).withStdinOpen(true).exec(); + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("/bin/sh", "-c", "sleep 1 && read line && echo $line") + .withTty(false) + .withStdinOpen(true) + .exec(); LOG.info("Created container: {}", container.toString()); assertThat(container.getId(), not(isEmptyString())); @@ -94,18 +97,27 @@ public void attachContainerWithStdin() throws Exception { assertTrue(inspectContainerResponse.getState().getRunning()); AttachContainerTestCallback callback = new AttachContainerTestCallback() { - @Override public void onNext(Frame frame) { assertEquals(frame.getStreamType(), StreamType.STDOUT); super.onNext(frame); - }; + } }; - InputStream stdin = new ByteArrayInputStream((snippet + "\n").getBytes()); + PipedOutputStream out = new PipedOutputStream(); + PipedInputStream in = new PipedInputStream(out); - dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true) - .withStdIn(stdin).exec(callback).awaitCompletion(2, TimeUnit.SECONDS); + dockerClient.attachContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .withFollowStream(true) + .withStdIn(in) + .exec(callback); + + out.write((snippet + "\n").getBytes()); + out.flush(); + + callback.awaitCompletion(15, SECONDS); callback.close(); assertThat(callback.toString(), containsString(snippet)); @@ -134,8 +146,12 @@ public void onNext(Frame frame) { }; }; - dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true) - .exec(callback).awaitCompletion(10, TimeUnit.SECONDS); + dockerClient.attachContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .withFollowStream(true) + .exec(callback) + .awaitCompletion(10, SECONDS); callback.close(); // HexDump.dump(collectFramesCallback.toString().getBytes(), 0, System.out, 0); diff --git a/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java index 66ac1bce3..690d478f4 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/AuthCmdExecTest.java @@ -44,14 +44,8 @@ public void testAuth() throws Exception { assertEquals(response.getStatus(), "Login Succeeded"); } - @Test() + @Test(expectedExceptions = UnauthorizedException.class) 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"); - } + DockerClientBuilder.getInstance(config("garbage")).build().authCmd().exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/BuildImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/BuildImageCmdExecTest.java index 92975f38f..f567dcfc8 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/BuildImageCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/BuildImageCmdExecTest.java @@ -1,11 +1,11 @@ package com.github.dockerjava.netty.exec; +import static com.github.dockerjava.utils.TestUtils.getVersion; 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; @@ -13,11 +13,13 @@ import java.io.InputStream; import java.lang.reflect.Method; import java.util.Collection; +import java.util.Collections; import java.util.UUID; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.TrueFileFilter; import org.testng.ITestResult; +import org.testng.SkipException; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeMethod; @@ -35,6 +37,7 @@ import com.github.dockerjava.api.model.PortBinding; import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.api.model.Ports.Binding; +import com.github.dockerjava.core.RemoteApiVersion; import com.github.dockerjava.core.command.BuildImageResultCallback; import com.github.dockerjava.core.command.PushImageResultCallback; import com.github.dockerjava.core.command.WaitContainerResultCallback; @@ -65,10 +68,9 @@ public void afterMethod(ITestResult result) { } @Test - public void testNginxDockerfileBuilder() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("nginx").getFile()); + public void author() throws Exception { - String imageId = buildImage(baseDir); + String imageId = buildImage(fileFromBuildTestResource("AUTHOR")); InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); assertThat(inspectImageResponse, not(nullValue())); @@ -77,27 +79,9 @@ public void testNginxDockerfileBuilder() throws Exception { assertThat(inspectImageResponse.getAuthor(), equalTo("Guillaume J. Charmes \"guillaume@dotcloud.com\"")); } - @Test(groups = "ignoreInCircleCi") - public void testNonstandard1() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader() - .getResource("nonstandard/subdirectory/Dockerfile-nonstandard").getFile()); - - buildImage(baseDir); - } - - @Test(groups = "ignoreInCircleCi") - public void testNonstandard2() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("nonstandard").getFile()); - File dockerFile = new File(Thread.currentThread().getContextClassLoader() - .getResource("nonstandard/subdirectory/Dockerfile-nonstandard").getFile()); - - dockerClient.buildImageCmd().withBaseDirectory(baseDir).withDockerfile(dockerFile).withNoCache(true) - .exec(new BuildImageResultCallback()).awaitImageId(); - } - @Test - public void testDockerBuilderFromTar() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFile").getFile()); + public void buildImageFromTar() throws Exception { + File baseDir = fileFromBuildTestResource("ADD/file"); Collection files = FileUtils.listFiles(baseDir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); File tarFile = CompressArchiveUtil.archiveTARFiles(baseDir, files, UUID.randomUUID().toString()); String response = dockerfileBuild(new FileInputStream(tarFile)); @@ -105,41 +89,43 @@ public void testDockerBuilderFromTar() throws Exception { } @Test - public void testDockerBuilderAddUrl() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddUrl").getFile()); + public void onBuild() throws Exception { + File baseDir = fileFromBuildTestResource("ONBUILD/parent"); + + dockerClient.buildImageCmd(baseDir).withNoCache(true).withTag("docker-java-onbuild") + .exec(new BuildImageResultCallback()).awaitImageId(); + baseDir = fileFromBuildTestResource("ONBUILD/child"); String response = dockerfileBuild(baseDir); - assertThat(response, containsString("Docker")); + assertThat(response, containsString("Successfully executed testrun.sh")); } @Test - public void testDockerBuilderAddFileInSubfolder() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFileInSubfolder") - .getFile()); + public void addUrl() throws Exception { + File baseDir = fileFromBuildTestResource("ADD/url"); String response = dockerfileBuild(baseDir); - assertThat(response, containsString("Successfully executed testrun.sh")); + assertThat(response, containsString("Example Domain")); } @Test - public void testDockerBuilderAddFilesViaWildcard() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFilesViaWildcard") - .getFile()); + public void addFileInSubfolder() throws Exception { + File baseDir = fileFromBuildTestResource("ADD/fileInSubfolder"); String response = dockerfileBuild(baseDir); - assertThat(response, containsString("Successfully executed testinclude1.sh")); - assertThat(response, not(containsString("Successfully executed testinclude2.sh"))); + assertThat(response, containsString("Successfully executed testrun.sh")); } @Test - public void testDockerBuilderAddFolder() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testAddFolder").getFile()); + public void addFilesViaWildcard() throws Exception { + File baseDir = fileFromBuildTestResource("ADD/filesViaWildcard"); String response = dockerfileBuild(baseDir); - assertThat(response, containsString("Successfully executed testAddFolder.sh")); + assertThat(response, containsString("Successfully executed testinclude1.sh")); + assertThat(response, not(containsString("Successfully executed testinclude2.sh"))); } @Test - public void testDockerBuilderEnv() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testEnv").getFile()); + public void addFolder() throws Exception { + File baseDir = fileFromBuildTestResource("ADD/folder"); String response = dockerfileBuild(baseDir); - assertThat(response, containsString("Successfully executed testrun.sh")); + assertThat(response, containsString("Successfully executed testAddFolder.sh")); } private String dockerfileBuild(InputStream tarInputStream) throws Exception { @@ -168,80 +154,42 @@ private String execBuild(BuildImageCmd buildImageCmd) throws Exception { } @Test(expectedExceptions = {DockerClientException.class}) - public void testDockerfileIgnored() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testDockerfileIgnored") - .getFile()); + public void dockerignoreDockerfileIgnored() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/DockerfileIgnored"); dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()).awaitImageId(); } @Test - public void testDockerfileNotIgnored() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testDockerfileNotIgnored") - .getFile()); + public void dockerignoreDockerfileNotIgnored() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/DockerfileNotIgnored"); dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()).awaitImageId(); } @Test(expectedExceptions = {DockerClientException.class}) - public void testInvalidDockerIgnorePattern() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader() - .getResource("testInvalidDockerignorePattern").getFile()); + public void dockerignoreInvalidDockerIgnorePattern() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/InvalidDockerignorePattern"); dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()).awaitImageId(); } - @Test(groups = "ignoreInCircleCi") - public void testDockerIgnore() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testDockerignore") - .getFile()); + @Test() + public void dockerignoreValidDockerIgnorePattern() throws Exception { + File baseDir = fileFromBuildTestResource("dockerignore/ValidDockerignorePattern"); String response = dockerfileBuild(baseDir); assertThat(response, containsString("/tmp/a/a /tmp/a/c /tmp/a/d")); } @Test - public void testNetCatDockerfileBuilder() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("netcat").getFile()); - - String imageId = dockerClient.buildImageCmd(baseDir).withNoCache(true).exec(new BuildImageResultCallback()) - .awaitImageId(); - - 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 Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testENVSubstitution") - .getFile()); + public void env() throws Exception { + File baseDir = fileFromBuildTestResource("ENV"); String response = dockerfileBuild(baseDir); assertThat(response, containsString("testENVSubstitution successfully completed")); } @Test - public void testBuildFromPrivateRegistry() throws Exception { + public void fromPrivateRegistry() throws Exception { File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("privateRegistry").getFile()); String imageId = buildImage(baseDir); @@ -282,17 +230,13 @@ public void testBuildFromPrivateRegistry() throws Exception { dockerClient.removeImageCmd("localhost:5000/testuser/busybox").withForce(true).exec(); - baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testBuildFromPrivateRegistry") - .getFile()); + baseDir = fileFromBuildTestResource("FROM/privateRegistry"); AuthConfigurations authConfigurations = new AuthConfigurations(); authConfigurations.addConfig(authConfig); - imageId = dockerClient.buildImageCmd(baseDir) - .withNoCache(true) - .withBuildAuthConfigs(authConfigurations) - .exec(new BuildImageResultCallback()) - .awaitImageId(); + imageId = dockerClient.buildImageCmd(baseDir).withNoCache(true).withBuildAuthConfigs(authConfigurations) + .exec(new BuildImageResultCallback()).awaitImageId(); inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); assertThat(inspectImageResponse, not(nullValue())); @@ -301,8 +245,8 @@ public void testBuildFromPrivateRegistry() throws Exception { } @Test - public void testBuildArgs() throws Exception { - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("testBuildArgs").getFile()); + public void buildArgs() throws Exception { + File baseDir = fileFromBuildTestResource("buildArgs"); String imageId = dockerClient.buildImageCmd(baseDir).withNoCache(true).withBuildArg("testArg", "abc") .exec(new BuildImageResultCallback()) @@ -314,4 +258,41 @@ public void testBuildArgs() throws Exception { assertThat(inspectImageResponse.getConfig().getLabels().get("test"), equalTo("abc")); } + + @Test + public void labels() throws Exception { + if (!getVersion(dockerClient).isGreaterOrEqual(RemoteApiVersion.VERSION_1_23)) { + throw new SkipException("API version should be >= 1.23"); + } + + File baseDir = fileFromBuildTestResource("labels"); + + String imageId = dockerClient.buildImageCmd(baseDir).withNoCache(true) + .withLabels(Collections.singletonMap("test", "abc")) + .exec(new BuildImageResultCallback()) + .awaitImageId(); + + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); + assertThat(inspectImageResponse, not(nullValue())); + LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + assertThat(inspectImageResponse.getConfig().getLabels().get("test"), equalTo("abc")); + } + + public void dockerfileNotInBaseDirectory() throws Exception { + File baseDirectory = fileFromBuildTestResource("dockerfileNotInBaseDirectory"); + File dockerfile = fileFromBuildTestResource("dockerfileNotInBaseDirectory/dockerfileFolder/Dockerfile"); + BuildImageCmd command = dockerClient.buildImageCmd() + .withBaseDirectory(baseDirectory) + .withDockerfile(dockerfile); + + String response = execBuild(command); + + assertThat(response, containsString("Successfully executed testrun.sh")); + } + + private File fileFromBuildTestResource(String resource) { + return new File(Thread.currentThread().getContextClassLoader() + .getResource("buildTests/" + resource).getFile()); + } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/CommitCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CommitCmdExecTest.java index 0186f4233..3d1a66333 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/CommitCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/CommitCmdExecTest.java @@ -68,13 +68,10 @@ public void commit() throws DockerException { assertThat(inspectImageResponse.getParent(), equalTo(busyboxImg.getId())); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void commitNonExistingContainer() throws DockerException { - try { - dockerClient.commitCmd("non-existent").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + + dockerClient.commitCmd("non-existent").exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/ConnectToNetworkCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/ConnectToNetworkCmdExecTest.java index ee1a681b6..7e290a98e 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/ConnectToNetworkCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/ConnectToNetworkCmdExecTest.java @@ -3,6 +3,8 @@ import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.command.CreateNetworkResponse; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.exception.DockerException; +import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.ContainerNetwork; import com.github.dockerjava.api.model.Network; import com.github.dockerjava.netty.AbstractNettyDockerClientTest; @@ -17,6 +19,10 @@ import java.lang.reflect.Method; import java.util.Collections; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + @Test(groups = "integration") public class ConnectToNetworkCmdExecTest extends AbstractNettyDockerClientTest { @@ -61,38 +67,49 @@ public void connectToNetwork() throws InterruptedException { @Test public void connectToNetworkWithContainerNetwork() throws InterruptedException { + final String NETWORK_SUBNET = "10.100.101.0/24"; + final String NETWORK_NAME = "nettyTestNetwork"; + final String CONTAINER_IP = "10.100.101.100"; + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("sleep", "9999") + .exec(); - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); dockerClient.startContainerCmd(container.getId()).exec(); + try { + dockerClient.removeNetworkCmd(NETWORK_NAME).exec(); + } catch (DockerException ignore) { + } + CreateNetworkResponse network = dockerClient.createNetworkCmd() - .withName("testNetwork") + .withName(NETWORK_NAME) .withIpam(new Network.Ipam() - .withConfig(new Network.Ipam.Config() - .withSubnet("10.100.100.0/24"))) + .withConfig(new Network.Ipam.Config() + .withSubnet(NETWORK_SUBNET))) .exec(); dockerClient.connectToNetworkCmd() .withNetworkId(network.getId()) .withContainerId(container.getId()) .withContainerNetwork(new ContainerNetwork() - .withAliases("testing") - .withIpamConfig(new ContainerNetwork.Ipam() - .withIpv4Address("10.100.100.100"))) + .withAliases("aliasName") + .withIpamConfig(new ContainerNetwork.Ipam() + .withIpv4Address(CONTAINER_IP))) .exec(); Network updatedNetwork = dockerClient.inspectNetworkCmd().withNetworkId(network.getId()).exec(); Network.ContainerNetworkConfig containerNetworkConfig = updatedNetwork.getContainers().get(container.getId()); assertNotNull(containerNetworkConfig); - assertEquals(containerNetworkConfig.getIpv4Address(), "10.100.100.100/24"); + assertThat(containerNetworkConfig.getIpv4Address(), is(CONTAINER_IP + "/24")); InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); - ContainerNetwork testNetwork = inspectContainerResponse.getNetworkSettings().getNetworks().get("testNetwork"); + ContainerNetwork testNetwork = inspectContainerResponse.getNetworkSettings().getNetworks().get(NETWORK_NAME); assertNotNull(testNetwork); - assertEquals(testNetwork.getAliases(), Collections.singletonList("testing")); - assertEquals(testNetwork.getGateway(), "10.100.100.1"); - assertEquals(testNetwork.getIpAddress(), "10.100.100.100"); + assertThat(testNetwork.getAliases(), hasItem("aliasName")); + assertEquals(testNetwork.getGateway(), "10.100.101.1"); + assertEquals(testNetwork.getIpAddress(), CONTAINER_IP); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExecTest.java index 57c4b46a0..c21c426b5 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/ContainerDiffCmdExecTest.java @@ -68,13 +68,10 @@ public void testContainerDiff() throws DockerException { assertThat(testChangeLog, hasField("kind", equalTo(1))); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void testContainerDiffWithNonExistingContainer() throws DockerException { - try { - dockerClient.containerDiffCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + + dockerClient.containerDiffCmd("non-existing").exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExecTest.java index caec336a2..02b56156d 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveFromContainerCmdExecTest.java @@ -70,13 +70,10 @@ public void copyFromContainer() throws Exception { assertTrue(responseAsString.length() > 0); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void copyFromNonExistingContainer() throws Exception { - try { - dockerClient.copyArchiveFromContainerCmd("non-existing", "/test").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException ignored) { - } + + dockerClient.copyArchiveFromContainerCmd("non-existing", "/test").exec(); } @Test diff --git a/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExecTest.java index eb43b54de..d75d36ea8 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/CopyArchiveToContainerCmdExecTest.java @@ -84,14 +84,10 @@ private void assertFileCopied(CreateContainerResponse container) throws IOExcept } } - @Test + @Test(expectedExceptions = NotFoundException.class) public void copyToNonExistingContainer() throws Exception { - try { - dockerClient.copyArchiveToContainerCmd("non-existing").withHostResource("src/test/resources/testReadFile") - .exec(); - fail("expected NotFoundException"); - } catch (NotFoundException ignored) { - } + + dockerClient.copyArchiveToContainerCmd("non-existing").withHostResource("src/test/resources/testReadFile").exec(); } @Test diff --git a/src/test/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExecTest.java index b9682c592..1060ae82a 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/CopyFileFromContainerCmdExecTest.java @@ -1,5 +1,6 @@ package com.github.dockerjava.netty.exec; +import static com.github.dockerjava.utils.TestUtils.getVersion; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.not; @@ -7,7 +8,9 @@ import java.io.InputStream; import java.lang.reflect.Method; +import com.github.dockerjava.core.RemoteApiVersion; import org.testng.ITestResult; +import org.testng.SkipException; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeMethod; @@ -43,6 +46,10 @@ public void afterMethod(ITestResult result) { @Test public void copyFromContainer() throws Exception { + if (getVersion(dockerClient).isGreaterOrEqual(RemoteApiVersion.VERSION_1_24)) { + throw new SkipException("Doesn't work since 1.24"); + } + // TODO extract this into a shared method CreateContainerResponse container = dockerClient.createContainerCmd("busybox") .withName("docker-java-itest-copyFromContainer").withCmd("touch", "/copyFromContainer").exec(); @@ -62,12 +69,9 @@ public void copyFromContainer() throws Exception { assertTrue(responseAsString.length() > 0); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void copyFromNonExistingContainer() throws Exception { - try { - dockerClient.copyFileFromContainerCmd("non-existing", "/test").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException ignored) { - } + + dockerClient.copyFileFromContainerCmd("non-existing", "/test").exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java index 3dbe21f16..aa97b8839 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/CreateContainerCmdExecTest.java @@ -2,6 +2,7 @@ import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.command.CreateNetworkResponse; +import com.github.dockerjava.api.command.CreateVolumeResponse; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.ConflictException; import com.github.dockerjava.api.exception.DockerException; @@ -9,18 +10,22 @@ import com.github.dockerjava.api.model.ContainerNetwork; 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.LogConfig; import com.github.dockerjava.api.model.Network; import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.api.model.Ports.Binding; +import com.github.dockerjava.core.RemoteApiVersion; import com.github.dockerjava.api.model.RestartPolicy; import com.github.dockerjava.api.model.Ulimit; import com.github.dockerjava.api.model.Volume; import com.github.dockerjava.api.model.VolumesFrom; import com.github.dockerjava.netty.AbstractNettyDockerClientTest; +import org.apache.commons.io.FileUtils; import org.testng.ITestResult; +import org.testng.SkipException; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeMethod; @@ -38,11 +43,13 @@ import static com.github.dockerjava.api.model.Capability.MKNOD; import static com.github.dockerjava.api.model.Capability.NET_ADMIN; +import static com.github.dockerjava.utils.TestUtils.getVersion; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItemInArray; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isEmptyString; @@ -135,6 +142,39 @@ public void createContainerWithReadOnlyVolume() throws DockerException { // assertFalse(inspectContainerResponse.getMounts().get(0).getRW()); } + @Test + public void createContainerWithNoCopyVolumes() throws DockerException { + final RemoteApiVersion apiVersion = getVersion(dockerClient); + + if (!apiVersion.isGreaterOrEqual(RemoteApiVersion.VERSION_1_23)) { + throw new SkipException("API version should be >= 1.23"); + } + + Volume volume1 = new Volume("/opt/webapp1"); + String container1Name = UUID.randomUUID().toString(); + + CreateVolumeResponse volumeResponse = dockerClient.createVolumeCmd().withName("webapp1").exec(); + assertThat(volumeResponse.getName(), equalTo("webapp1")); + assertThat(volumeResponse.getDriver(), equalTo("local")); + assertThat(volumeResponse.getMountpoint(), containsString("/webapp1/")); + + Bind bind1 = new Bind("webapp1", volume1, true); + + CreateContainerResponse container1 = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999") + .withName(container1Name) + .withBinds(bind1).exec(); + LOG.info("Created container1 {}", container1.toString()); + + InspectContainerResponse inspectContainerResponse1 = dockerClient.inspectContainerCmd(container1.getId()).exec(); + + assertThat(Arrays.asList(inspectContainerResponse1.getHostConfig().getBinds()), contains(bind1)); + assertThat(inspectContainerResponse1, mountedVolumes(contains(volume1))); + + assertThat(inspectContainerResponse1.getMounts().get(0).getDestination(), equalTo(volume1)); + assertThat(inspectContainerResponse1.getMounts().get(0).getMode(), equalTo("rw,nocopy")); + assertThat(inspectContainerResponse1.getMounts().get(0).getRW(), equalTo(true)); + } + @Test public void createContainerWithVolumesFrom() throws DockerException { @@ -189,9 +229,12 @@ public void createContainerWithVolumesFrom() throws DockerException { @Test public void createContainerWithEnv() throws Exception { + final String testVariable = "VARIABLE=success"; - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withEnv("VARIABLE=success") - .withCmd("env").exec(); + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withEnv(testVariable) + .withCmd("env") + .exec(); LOG.info("Created container {}", container.toString()); @@ -199,11 +242,11 @@ public void createContainerWithEnv() throws Exception { InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); - assertThat(Arrays.asList(inspectContainerResponse.getConfig().getEnv()), containsInAnyOrder("VARIABLE=success")); + assertThat(Arrays.asList(inspectContainerResponse.getConfig().getEnv()), hasItem(testVariable)); dockerClient.startContainerCmd(container.getId()).exec(); - assertThat(containerLog(container.getId()), containsString("VARIABLE=success")); + assertThat(containerLog(container.getId()), containsString(testVariable)); } @Test @@ -364,7 +407,7 @@ public void createContainerWithAlias() throws DockerException { .exec(); ContainerNetwork aliasNet = inspectContainerResponse.getNetworkSettings().getNetworks().get("aliasNet"); - assertEquals(aliasNet.getAliases(), Collections.singletonList("server")); + assertThat(aliasNet.getAliases(), hasItem("server")); } @Test @@ -672,4 +715,20 @@ public void createContainerWithCgroupParent() throws DockerException { assertThat(inspectContainer.getHostConfig().getCgroupParent(), is("/parent")); } + + @SuppressWarnings("Duplicates") + @Test + public void createContainerWithShmSize() throws DockerException { + HostConfig hostConfig = new HostConfig().withShmSize(96 * FileUtils.ONE_MB); + CreateContainerResponse container = dockerClient.createContainerCmd(BUSYBOX_IMAGE) + .withHostConfig(hostConfig).withCmd("true").exec(); + + LOG.info("Created container {}", container.toString()); + + assertThat(container.getId(), not(isEmptyString())); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + + assertEquals(inspectContainerResponse.getHostConfig().getShmSize(), hostConfig.getShmSize()); + } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/EventsCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/EventsCmdExecTest.java index a634e2562..2a446569d 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/EventsCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/EventsCmdExecTest.java @@ -22,8 +22,6 @@ @Test(groups = "integration") public class EventsCmdExecTest extends AbstractNettyDockerClientTest { - private static int KNOWN_NUM_EVENTS = 4; - private static String getEpochTime() { return String.valueOf(System.currentTimeMillis() / 1000); } @@ -48,9 +46,6 @@ public void afterMethod(ITestResult result) { super.afterMethod(result); } - /* - * This specific test may fail with boot2docker as time may not in sync with host system - */ @Test public void testEventStreamTimeBound() throws Exception { // Don't include other tests events @@ -60,82 +55,84 @@ public void testEventStreamTimeBound() throws Exception { int expectedEvents = generateEvents(); String endTime = getEpochTime(); - CountDownLatch countDownLatch = new CountDownLatch(expectedEvents); - EventsTestCallback eventCallback = new EventsTestCallback(countDownLatch); - - dockerClient.eventsCmd().withSince(startTime).withUntil(endTime).exec(eventCallback); - - Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); - - eventCallback.close(); - - assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); - } - - @Test - public void testEventStreaming1() throws Exception { - // Don't include other tests events - TimeUnit.SECONDS.sleep(1); - - CountDownLatch countDownLatch = new CountDownLatch(KNOWN_NUM_EVENTS); - EventsTestCallback eventCallback = new EventsTestCallback(countDownLatch); + EventsTestCallback eventCallback = new EventsTestCallback(expectedEvents); - dockerClient.eventsCmd().withSince(getEpochTime()).exec(eventCallback); + dockerClient.eventsCmd() + .withSince(startTime) + .withUntil(endTime) + .exec(eventCallback); - generateEvents(); - - Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); + List events = eventCallback.awaitExpectedEvents(3, TimeUnit.MINUTES); - eventCallback.close(); - assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); + // we may receive more events as expected + assertTrue(events.size() >= expectedEvents, "Received events: " + events); } @Test - public void testEventStreaming2() throws Exception { + public void testEventStreaming() throws Exception { // Don't include other tests events TimeUnit.SECONDS.sleep(1); + + String startTime = getEpochTime(); + int expectedEvents = generateEvents(); - CountDownLatch countDownLatch = new CountDownLatch(KNOWN_NUM_EVENTS); - EventsTestCallback eventCallback = new EventsTestCallback(countDownLatch); + EventsTestCallback eventCallback = new EventsTestCallback(expectedEvents); - dockerClient.eventsCmd().withSince(getEpochTime()).exec(eventCallback); + dockerClient.eventsCmd() + .withSince(startTime) + .exec(eventCallback); generateEvents(); - Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); - - eventCallback.close(); - assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); + List events = eventCallback.awaitExpectedEvents(3, TimeUnit.MINUTES); + + // we may receive more events as expected + assertTrue(events.size() >= expectedEvents, "Received events: " + events); } + public void testEventStreamingWithFilter() throws Exception { // Don't include other tests events TimeUnit.SECONDS.sleep(1); + + String startTime = getEpochTime(); + int expectedEvents = 1; - CountDownLatch countDownLatch = new CountDownLatch(1); - EventsTestCallback eventCallback = dockerClient.eventsCmd().withEventFilter("start") - .exec(new EventsTestCallback(countDownLatch)); + EventsTestCallback eventCallback = new EventsTestCallback(expectedEvents); + + dockerClient.eventsCmd() + .withSince(startTime) + .withEventFilter("start") + .exec(eventCallback); generateEvents(); - Boolean zeroCount = countDownLatch.await(10, TimeUnit.SECONDS); - - eventCallback.close(); - assertTrue(zeroCount, "Received only: " + eventCallback.getEvents()); + List events = eventCallback.awaitExpectedEvents(3, TimeUnit.MINUTES); + + // we should get exactly one "start" event here + assertEquals(events.size(), expectedEvents, "Received events: " + events); } /** - * This method generates {#link KNOWN_NUM_EVENTS} events + * This method generates some events and returns the number of events being generated */ private int generateEvents() throws Exception { - String testImage = "busybox"; + String testImage = "busybox:latest"; dockerClient.pullImageCmd(testImage).exec(new PullImageResultCallback()).awaitSuccess(); - CreateContainerResponse container = dockerClient.createContainerCmd(testImage).withCmd("sleep", "9999").exec(); dockerClient.startContainerCmd(container.getId()).exec(); - dockerClient.stopContainerCmd(container.getId()).exec(); - return KNOWN_NUM_EVENTS; + dockerClient.stopContainerCmd(container.getId()).withTimeout(1).exec(); + + // generates 5 events with remote api 1.24: + + // Event[status=pull,id=busybox:latest,from=,node=,type=IMAGE,action=pull,actor=com.github.dockerjava.api.model.EventActor@417db6d7[id=busybox:latest,attributes={name=busybox}],time=1473455186,timeNano=1473455186436681587] + // Event[status=create,id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,from=busybox:latest,node=,type=CONTAINER,action=create,actor=com.github.dockerjava.api.model.EventActor@40bcec[id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,attributes={image=busybox:latest, name=sick_lamport}],time=1473455186,timeNano=1473455186470713257] + // Event[status=,id=,from=,node=,type=NETWORK,action=connect,actor=com.github.dockerjava.api.model.EventActor@318a1b01[id=10870ceb13abb7cf841ea68868472da881b33c8ed08d2cde7dbb39d7c24d1d27,attributes={container=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c, name=bridge, type=bridge}],time=1473455186,timeNano=1473455186544318466] + // Event[status=start,id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,from=busybox:latest,node=,type=CONTAINER,action=start,actor=com.github.dockerjava.api.model.EventActor@606f43a3[id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,attributes={image=busybox:latest, name=sick_lamport}],time=1473455186,timeNano=1473455186786163819] + // Event[status=kill,id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,from=busybox:latest,node=,type=CONTAINER,action=kill,actor=com.github.dockerjava.api.model.EventActor@72a9ffcf[id=6ec10182cde227040bfead8547b63105e6bbc4e94b99f6098bfad6e158ce0d3c,attributes={image=busybox:latest, name=sick_lamport, signal=15}],time=1473455186,timeNano=1473455186792963392] + + return 5; } private class EventsTestCallback extends EventsResultCallback { @@ -144,18 +141,25 @@ private class EventsTestCallback extends EventsResultCallback { private final List events = new ArrayList(); - public EventsTestCallback(CountDownLatch countDownLatch) { - this.countDownLatch = countDownLatch; + public EventsTestCallback(int expextedEvents) { + this.countDownLatch = new CountDownLatch(expextedEvents); } public void onNext(Event event) { LOG.info("Received event #{}: {}", countDownLatch.getCount(), event); - countDownLatch.countDown(); events.add(event); + countDownLatch.countDown(); } - - public List getEvents() { - return new ArrayList(events); + + public List awaitExpectedEvents(long timeout, TimeUnit unit ) { + try { + countDownLatch.await(timeout, unit); + close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return new ArrayList(events); } } } + diff --git a/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java index 2938ac8c0..52db7fe44 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/ExecStartCmdExecTest.java @@ -1,5 +1,8 @@ package com.github.dockerjava.netty.exec; +import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_22; +import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_23; +import static com.github.dockerjava.utils.TestUtils.getVersion; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.not; @@ -12,6 +15,7 @@ import java.security.SecureRandom; import java.util.concurrent.TimeUnit; +import com.github.dockerjava.core.RemoteApiVersion; import org.testng.ITestResult; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; @@ -46,7 +50,7 @@ public void afterMethod(ITestResult result) { super.afterMethod(result); } - @Test(groups = "ignoreInCircleCi") + @Test() public void execStart() throws Exception { String containerName = "generated_" + new SecureRandom().nextInt(); @@ -58,9 +62,14 @@ public void execStart() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) - .withAttachStdout(true).withCmd("touch", "/execStartTest.log").exec(); - dockerClient.execStartCmd(execCreateCmdResponse.getId()).withDetach(false) - .exec(new ExecStartResultCallback(System.out, System.err)).awaitCompletion(); + .withAttachStdout(true) + .withCmd("touch", "/execStartTest.log") + .exec(); + + dockerClient.execStartCmd(execCreateCmdResponse.getId()) + .withDetach(false) + .exec(new ExecStartResultCallback(System.out, System.err)) + .awaitCompletion(); InputStream response = dockerClient.copyArchiveFromContainerCmd(container.getId(), "/execStartTest.log").exec(); @@ -73,7 +82,7 @@ public void execStart() throws Exception { assertTrue(responseAsString.length() > 0); } - @Test(groups = "ignoreInCircleCi") + @Test() public void execStartAttached() throws Exception { String containerName = "generated_" + new SecureRandom().nextInt(); @@ -85,9 +94,14 @@ public void execStartAttached() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) - .withAttachStdout(true).withCmd("touch", "/execStartTest.log").exec(); - dockerClient.execStartCmd(execCreateCmdResponse.getId()).withDetach(false) - .exec(new ExecStartResultCallback(System.out, System.err)).awaitCompletion(); + .withAttachStdout(true) + .withCmd("touch", "/execStartTest.log") + .exec(); + + dockerClient.execStartCmd(execCreateCmdResponse.getId()) + .withDetach(false) + .exec(new ExecStartResultCallback(System.out, System.err)) + .awaitCompletion(); InputStream response = dockerClient.copyArchiveFromContainerCmd(container.getId(), "/execStartTest.log").exec(); Boolean bytesAvailable = response.available() > 0; @@ -99,7 +113,7 @@ public void execStartAttached() throws Exception { assertTrue(responseAsString.length() > 0); } - @Test(groups = "ignoreInCircleCi") + @Test() public void execStartAttachStdin() throws Exception { String containerName = "generated_" + new SecureRandom().nextInt(); @@ -115,14 +129,23 @@ public void execStartAttachStdin() throws Exception { ByteArrayOutputStream stdout = new ByteArrayOutputStream(); ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) - .withAttachStdout(true).withAttachStdin(true).withCmd("cat").exec(); - boolean completed = dockerClient.execStartCmd(execCreateCmdResponse.getId()).withDetach(false).withTty(true).withStdIn(stdin) - .exec(new ExecStartResultCallback(stdout, System.err)).awaitCompletion(5, TimeUnit.SECONDS); + .withAttachStdout(true) + .withAttachStdin(true) + .withCmd("cat") + .exec(); + + boolean completed = dockerClient.execStartCmd(execCreateCmdResponse.getId()) + .withDetach(false) + .withTty(true) + .withStdIn(stdin) + .exec(new ExecStartResultCallback(stdout, System.err)) + .awaitCompletion(5, TimeUnit.SECONDS); assertTrue(completed, "The process was not finished."); assertEquals(stdout.toString("UTF-8"), "STDIN\n"); } + @Test() public void execStartAttachStdinToShell() throws Exception { String containerName = "generated_" + new SecureRandom().nextInt(); @@ -156,7 +179,7 @@ public void execStartAttachStdinToShell() throws Exception { assertThat(stdout.toString(), containsString("etc\n")); } - @Test(groups = "ignoreInCircleCi") + @Test() public void execStartNotAttachedStdin() throws Exception { String containerName = "generated_" + new SecureRandom().nextInt(); @@ -172,11 +195,24 @@ public void execStartNotAttachedStdin() throws Exception { ByteArrayOutputStream stdout = new ByteArrayOutputStream(); ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(container.getId()) - .withAttachStdout(true).withAttachStdin(false).withCmd("/bin/sh").exec(); - boolean completed = dockerClient.execStartCmd(execCreateCmdResponse.getId()).withDetach(false).withStdIn(stdin) - .exec(new ExecStartResultCallback(stdout, System.err)).awaitCompletion(5, TimeUnit.SECONDS); + .withAttachStdout(true) + .withAttachStdin(false) + .withCmd("/bin/sh") + .exec(); + + boolean completed = dockerClient.execStartCmd(execCreateCmdResponse.getId()) + .withDetach(false) + .withStdIn(stdin) + .exec(new ExecStartResultCallback(stdout, System.err)) + .awaitCompletion(5, TimeUnit.SECONDS); - assertTrue(completed, "The process was not finished."); assertEquals(stdout.toString(), ""); + + if (getVersion(dockerClient).isGreaterOrEqual(VERSION_1_23)) { + assertFalse(completed, "The process was not finished."); + } else { + assertTrue(completed, "with v1.22 of the remote api the server closed the connection when no stdin " + + "was attached while exec create, so completed was true"); + } } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/KillContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/KillContainerCmdExecTest.java index 867373e63..d354c8df3 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/KillContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/KillContainerCmdExecTest.java @@ -67,14 +67,10 @@ public void killContainer() throws DockerException { } - @Test + @Test(expectedExceptions = NotFoundException.class) public void killNonExistingContainer() throws DockerException { - try { - dockerClient.killContainerCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + dockerClient.killContainerCmd("non-existing").exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/LoadImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/LoadImageCmdExecTest.java new file mode 100644 index 000000000..d978f5662 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/LoadImageCmdExecTest.java @@ -0,0 +1,76 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.model.Image; + +import com.github.dockerjava.netty.AbstractNettyDockerClientTest; +import com.github.dockerjava.utils.TestResources; +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.InputStream; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.hamcrest.core.IsEqual.equalTo; + +@Test(groups = "integration") +public class LoadImageCmdExecTest extends AbstractNettyDockerClientTest { + + private String expectedImageId; + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + expectedImageId = "sha256:56031f66eb0cef2e2e5cb2d1dabafaa0ebcd0a18a507d313b5bdb8c0472c5eba"; + if (findImageWithId(expectedImageId, dockerClient.listImagesCmd().exec()) != null) { + dockerClient.removeImageCmd(expectedImageId).exec(); + } + } + + @AfterMethod + public void afterMethod(ITestResult result) { + dockerClient.removeImageCmd(expectedImageId).exec(); + super.afterMethod(result); + } + + @Test + public void loadImageFromTar() throws Exception { + try (InputStream uploadStream = Files.newInputStream(TestResources.getApiImagesLoadTestTarball())) { + dockerClient.loadImageCmd(uploadStream).exec(); + } + + final Image image = findImageWithId(expectedImageId, dockerClient.listImagesCmd().exec()); + + assertThat("Can't find expected image after loading from a tar archive!", image, notNullValue()); + assertThat("Image after loading from a tar archive has wrong tags!", + asList(image.getRepoTags()), equalTo(singletonList("docker-java/load:1.0"))); + } + + private Image findImageWithId(final String id, final List images) { + for (Image image : images) { + if (id.equals(image.getId())) { + return image; + } + } + return null; + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java index 310994755..fe408ac18 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/LogContainerCmdExecTest.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; import org.testng.ITestResult; import org.testng.annotations.AfterMethod; @@ -18,6 +19,7 @@ import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.model.StreamType; import com.github.dockerjava.core.command.WaitContainerResultCallback; import com.github.dockerjava.netty.AbstractNettyDockerClientTest; @@ -45,31 +47,65 @@ public void afterMethod(ITestResult result) { } @Test - public void asyncLogContainer() throws Exception { + public void asyncLogContainerWithTtyEnabled() throws Exception { - String snippet = "hello world"; - - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/echo", snippet) + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("/bin/sh", "-c", "while true; do echo hello; sleep 1; done") + .withTty(true) .exec(); LOG.info("Created container: {}", container.toString()); assertThat(container.getId(), not(isEmptyString())); - dockerClient.startContainerCmd(container.getId()).exec(); + dockerClient.startContainerCmd(container.getId()) + .exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) - .awaitStatusCode(); + LogContainerTestCallback loggingCallback = new LogContainerTestCallback(true); - assertThat(exitCode, equalTo(0)); + // this essentially test the since=0 case + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .withFollowStream(true) + .withTailAll() + .exec(loggingCallback); - LogContainerTestCallback loggingCallback = new LogContainerTestCallback(); + loggingCallback.awaitCompletion(3, TimeUnit.SECONDS); + + assertTrue(loggingCallback.toString().contains("hello")); + + assertEquals(loggingCallback.getCollectedFrames().get(0).getStreamType(), StreamType.RAW); + } + + @Test + public void asyncLogContainerWithTtyDisabled() throws Exception { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("/bin/sh", "-c", "while true; do echo hello; sleep 1; done") + .withTty(false) + .exec(); + + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.startContainerCmd(container.getId()) + .exec(); + + LogContainerTestCallback loggingCallback = new LogContainerTestCallback(true); // this essentially test the since=0 case - dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .withFollowStream(true) + .withTailAll() + .exec(loggingCallback); - loggingCallback.awaitCompletion(); + loggingCallback.awaitCompletion(3, TimeUnit.SECONDS); - assertTrue(loggingCallback.toString().contains(snippet)); + assertTrue(loggingCallback.toString().contains("hello")); + + assertEquals(loggingCallback.getCollectedFrames().get(0).getStreamType(), StreamType.STDOUT); } @Test @@ -106,7 +142,8 @@ public void asyncMultipleLogContainer() throws Exception { String snippet = "hello world"; - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/echo", snippet) + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("/bin/echo", snippet) .exec(); LOG.info("Created container: {}", container.toString()); @@ -114,26 +151,36 @@ public void asyncMultipleLogContainer() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + int exitCode = dockerClient.waitContainerCmd(container.getId()) + .exec(new WaitContainerResultCallback()) .awaitStatusCode(); assertThat(exitCode, equalTo(0)); LogContainerTestCallback loggingCallback = new LogContainerTestCallback(); - dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .exec(loggingCallback); loggingCallback.close(); loggingCallback = new LogContainerTestCallback(); - dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .exec(loggingCallback); loggingCallback.close(); loggingCallback = new LogContainerTestCallback(); - dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).exec(loggingCallback); + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .exec(loggingCallback); loggingCallback.awaitCompletion(); @@ -144,7 +191,8 @@ public void asyncMultipleLogContainer() throws Exception { public void asyncLogContainerWithSince() throws Exception { String snippet = "hello world"; - CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/echo", snippet) + CreateContainerResponse container = dockerClient.createContainerCmd("busybox") + .withCmd("/bin/echo", snippet) .exec(); LOG.info("Created container: {}", container.toString()); @@ -154,19 +202,22 @@ public void asyncLogContainerWithSince() throws Exception { dockerClient.startContainerCmd(container.getId()).exec(); - int exitCode = dockerClient.waitContainerCmd(container.getId()).exec(new WaitContainerResultCallback()) + int exitCode = dockerClient.waitContainerCmd(container.getId()) + .exec(new WaitContainerResultCallback()) .awaitStatusCode(); assertThat(exitCode, equalTo(0)); LogContainerTestCallback loggingCallback = new LogContainerTestCallback(); - dockerClient.logContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withSince(timestamp) + dockerClient.logContainerCmd(container.getId()) + .withStdErr(true) + .withStdOut(true) + .withSince(timestamp) .exec(loggingCallback); loggingCallback.awaitCompletion(); assertThat(loggingCallback.toString(), containsString(snippet)); } - } diff --git a/src/test/java/com/github/dockerjava/netty/exec/PauseCmdImplTest.java b/src/test/java/com/github/dockerjava/netty/exec/PauseCmdImplTest.java new file mode 100644 index 000000000..612c3efd2 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/PauseCmdImplTest.java @@ -0,0 +1,103 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.exception.InternalServerErrorException; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.netty.AbstractNettyDockerClientTest; +import com.github.dockerjava.utils.ContainerUtils; +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.lang.reflect.Method; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +@Test(groups = {"integration", "ignoreInCircleCi"}) +public class PauseCmdImplTest extends AbstractNettyDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(PauseCmdImplTest.class); + + @BeforeTest + public void beforeTest() throws Exception { + 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 pauseRunningContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.pauseContainer(dockerClient, container); + } + + @Test(expectedExceptions = NotFoundException.class) + public void pauseNonExistingContainer() { + + dockerClient.pauseContainerCmd("non-existing").exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void pauseStoppedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.stopContainer(dockerClient, container); + + dockerClient.pauseContainerCmd(container.getId()).exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void pausePausedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.pauseContainer(dockerClient, container); + + dockerClient.pauseContainerCmd(container.getId()).exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void pauseCreatedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.pauseContainerCmd(container.getId()).exec(); + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/PushImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/PushImageCmdExecTest.java index 777cc8eed..73e99af6d 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/PushImageCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/PushImageCmdExecTest.java @@ -21,7 +21,7 @@ import com.github.dockerjava.core.command.PushImageResultCallback; import com.github.dockerjava.netty.AbstractNettyDockerClientTest; -@Test(groups = "integration") +@Test(groups = {"integration", "integration-auth"}) public class PushImageCmdExecTest extends AbstractNettyDockerClientTest { public static final Logger LOG = LoggerFactory.getLogger(PushImageCmdExecTest.class); diff --git a/src/test/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExecTest.java index ffe456c09..1e7abebdb 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/RemoveContainerCmdExecTest.java @@ -69,13 +69,10 @@ public void removeContainer() throws DockerException { } - @Test + @Test(expectedExceptions = NotFoundException.class) public void removeNonExistingContainer() throws DockerException { - try { - dockerClient.removeContainerCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + + dockerClient.removeContainerCmd("non-existing").exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/RemoveImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/RemoveImageCmdExecTest.java index e2f69ac1e..857bec714 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/RemoveImageCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/RemoveImageCmdExecTest.java @@ -74,14 +74,10 @@ public void removeImage() throws DockerException, InterruptedException { assertThat(containers, matcher); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void removeNonExistingImage() throws DockerException, InterruptedException { - try { - dockerClient.removeImageCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + dockerClient.removeImageCmd("non-existing").exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/RemoveNetworkCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/RemoveNetworkCmdExecTest.java index 9fda49f21..f8e7d26ca 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/RemoveNetworkCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/RemoveNetworkCmdExecTest.java @@ -64,13 +64,10 @@ public void removeNetwork() throws DockerException { } - @Test + @Test(expectedExceptions = NotFoundException.class) public void removeNonExistingContainer() throws DockerException { - try { - dockerClient.removeNetworkCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + + dockerClient.removeNetworkCmd("non-existing").exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/RemoveVolumeCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/RemoveVolumeCmdExecTest.java index 59aafde67..d7995861c 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/RemoveVolumeCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/RemoveVolumeCmdExecTest.java @@ -41,7 +41,7 @@ public void afterMethod(ITestResult result) { super.afterMethod(result); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void removeVolume() throws DockerException { String volumeName = "volume1"; @@ -55,11 +55,6 @@ public void removeVolume() throws DockerException { dockerClient.removeVolumeCmd(volumeName).exec(); - try { - dockerClient.inspectVolumeCmd(volumeName).exec(); - fail("Expected NotFoundException"); - } catch (NotFoundException e) { - // just ignore - } + dockerClient.inspectVolumeCmd(volumeName).exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/RestartContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/RestartContainerCmdExecTest.java index 3360057a8..3b1ada5c2 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/RestartContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/RestartContainerCmdExecTest.java @@ -71,14 +71,10 @@ public void restartContainer() throws DockerException { dockerClient.killContainerCmd(container.getId()).exec(); } - @Test + @Test(expectedExceptions = NotFoundException.class) public void restartNonExistingContainer() throws DockerException, InterruptedException { - try { - dockerClient.restartContainerCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + dockerClient.restartContainerCmd("non-existing").exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/SaveImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/SaveImageCmdExecTest.java index 0527b793f..a2fab38a7 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/SaveImageCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/SaveImageCmdExecTest.java @@ -47,8 +47,9 @@ public void afterMethod(ITestResult result) { @Test public void saveImage() throws Exception { - InputStream image = IOUtils.toBufferedInputStream(dockerClient.saveImageCmd("busybox").exec()); - assertThat(image.available(), greaterThan(0)); + try (InputStream image = dockerClient.saveImageCmd("busybox").exec()) { + assertThat(image.available(), greaterThan(0)); + } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/StartContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/StartContainerCmdExecTest.java index f4a36c2b1..eed82fcd9 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/StartContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/StartContainerCmdExecTest.java @@ -254,7 +254,7 @@ public void startContainerWithRandomPortBindings() throws DockerException { } - @Test + @Test(expectedExceptions = InternalServerErrorException.class) public void startContainerWithConflictingPortBindings() throws DockerException { ExposedPort tcp22 = ExposedPort.tcp(22); @@ -271,13 +271,7 @@ public void startContainerWithConflictingPortBindings() throws DockerException { assertThat(container.getId(), not(isEmptyString())); - try { - dockerClient.startContainerCmd(container.getId()).exec(); - fail("expected InternalServerErrorException"); - } catch (InternalServerErrorException e) { - - } - + dockerClient.startContainerCmd(container.getId()).exec(); } @Test @@ -415,13 +409,10 @@ public void startContainer() throws DockerException { } } - @Test + @Test(expectedExceptions = NotFoundException.class) public void testStartNonExistingContainer() throws DockerException { - try { - dockerClient.startContainerCmd("non-existing").exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + + dockerClient.startContainerCmd("non-existing").exec(); } /** diff --git a/src/test/java/com/github/dockerjava/netty/exec/StopContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/StopContainerCmdExecTest.java index b3f545a42..063bcee30 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/StopContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/StopContainerCmdExecTest.java @@ -61,7 +61,7 @@ public void testStopContainer() throws DockerException { dockerClient.startContainerCmd(container.getId()).exec(); LOG.info("Stopping container: {}", container.getId()); - dockerClient.stopContainerCmd(container.getId()).withTimeout(2).exec(); + dockerClient.stopContainerCmd(container.getId()).withTimeout(10).exec(); InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); LOG.info("Container Inspect: {}", inspectContainerResponse.toString()); @@ -69,21 +69,15 @@ public void testStopContainer() throws DockerException { assertThat(inspectContainerResponse.getState().getRunning(), is(equalTo(false))); final Integer exitCode = inspectContainerResponse.getState().getExitCode(); - if (apiVersion.equals(VERSION_1_22)) { - assertThat(exitCode, is(0)); - } else { - assertThat(exitCode, not(0)); - } + + assertThat(exitCode, is(137)); + } - @Test + @Test(expectedExceptions = NotFoundException.class) public void testStopNonExistingContainer() throws DockerException { - try { - dockerClient.stopContainerCmd("non-existing").withTimeout(2).exec(); - fail("expected NotFoundException"); - } catch (NotFoundException e) { - } + dockerClient.stopContainerCmd("non-existing").withTimeout(10).exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/TagImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/TagImageCmdExecTest.java index bbab169ef..6c84775ee 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/TagImageCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/TagImageCmdExecTest.java @@ -49,15 +49,11 @@ public void tagImage() throws Exception { dockerClient.removeImageCmd("docker-java/busybox:" + tag).exec(); } - @Test + @Test(expectedExceptions = NotFoundException.class) 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) { - } + String tag = "" + RandomUtils.nextInt(Integer.MAX_VALUE); + dockerClient.tagImageCmd("non-existing", "docker-java/busybox", tag).exec(); } } diff --git a/src/test/java/com/github/dockerjava/netty/exec/UnpauseCmdImplTest.java b/src/test/java/com/github/dockerjava/netty/exec/UnpauseCmdImplTest.java new file mode 100644 index 000000000..cb30babc4 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/UnpauseCmdImplTest.java @@ -0,0 +1,118 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.exception.InternalServerErrorException; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.netty.AbstractNettyDockerClientTest; +import com.github.dockerjava.utils.ContainerUtils; +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.lang.reflect.Method; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.not; + +@Test(groups = {"integration", "ignoreInCircleCi"}) +public class UnpauseCmdImplTest extends AbstractNettyDockerClientTest { + + public static final Logger LOG = LoggerFactory.getLogger(UnpauseCmdImplTest.class); + + @BeforeTest + public void beforeTest() throws Exception { + 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 unpausePausedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.pauseContainer(dockerClient, container); + + ContainerUtils.unpauseContainer(dockerClient, container); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void unpauseRunningContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + dockerClient.unpauseContainerCmd(container.getId()).exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void unpauseStoppedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.stopContainer(dockerClient, container); + + dockerClient.unpauseContainerCmd(container.getId()).exec(); + } + + @Test(expectedExceptions = NotFoundException.class) + public void unpauseNonExistingContainer() { + + dockerClient.unpauseContainerCmd("non-existing").exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void unpauseCreatedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + dockerClient.unpauseContainerCmd(container.getId()).exec(); + } + + @Test(expectedExceptions = InternalServerErrorException.class) + public void unpauseUnpausedContainer() { + + CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("sleep", "9999").exec(); + LOG.info("Created container: {}", container.toString()); + assertThat(container.getId(), not(isEmptyString())); + + ContainerUtils.startContainer(dockerClient, container); + + ContainerUtils.pauseContainer(dockerClient, container); + + dockerClient.unpauseContainerCmd(container.getId()).exec(); + dockerClient.unpauseContainerCmd(container.getId()).exec(); + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/UpdateContainerCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/UpdateContainerCmdExecTest.java index e2907dc8a..681839c17 100644 --- a/src/test/java/com/github/dockerjava/netty/exec/UpdateContainerCmdExecTest.java +++ b/src/test/java/com/github/dockerjava/netty/exec/UpdateContainerCmdExecTest.java @@ -78,15 +78,15 @@ public void updateContainer() throws DockerException, IOException { // .withCpusetCpus("0") // depends on env .withCpusetMems("0") .withMemory(314572800L) - .withMemorySwap(514288000L) +// .withMemorySwap(514288000L) Your kernel does not support swap limit capabilities, memory limited without swap. .withMemoryReservation(209715200L) // .withKernelMemory(52428800) Can not update kernel memory to a running container, please stop it first. .exec(); - // docker toolbox 1.10.1 - assertThat(updateResponse.getWarnings(), hasSize(1)); - assertThat(updateResponse.getWarnings().get(0), - is("Your kernel does not support Block I/O weight. Weight discarded.")); + // found only on docker toolbox (1.10.1) +// assertThat(updateResponse.getWarnings(), hasSize(1)); +// assertThat(updateResponse.getWarnings().get(0), +// is("Your kernel does not support Block I/O weight. Weight discarded.")); InspectContainerResponse inspectAfter = dockerClient.inspectContainerCmd(containerId).exec(); final HostConfig afterHostConfig = inspectAfter.getHostConfig(); @@ -101,7 +101,7 @@ public void updateContainer() throws DockerException, IOException { assertThat(afterHostConfig.getCpusetMems(), is("0")); assertThat(afterHostConfig.getMemoryReservation(), is(209715200L)); - assertThat(afterHostConfig.getMemorySwap(), is(514288000L)); +// assertThat(afterHostConfig.getMemorySwap(), is(514288000L)); } } diff --git a/src/test/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandlerTest.java b/src/test/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandlerTest.java index 6652f3eba..eea9ddc0f 100644 --- a/src/test/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandlerTest.java +++ b/src/test/java/com/github/dockerjava/netty/handler/HttpResponseStreamHandlerTest.java @@ -1,13 +1,21 @@ package com.github.dockerjava.netty.handler; +import static com.github.dockerjava.netty.handler.HttpResponseStreamHandler.HttpResponseInputStream; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; -import java.io.InputStream; - import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + import org.apache.commons.io.IOUtils; import org.mockito.Mockito; import org.testng.annotations.Test; @@ -25,9 +33,81 @@ public void testNoBytesSkipped() throws Exception { ChannelHandlerContext ctx = Mockito.mock(ChannelHandlerContext.class); ByteBuf buffer = generateByteBuf(); streamHandler.channelRead0(ctx, buffer); - streamHandler.channelReadComplete(ctx); + streamHandler.channelInactive(ctx); + + try (InputStream inputStream = callback.getInputStream()) { + assertTrue(IOUtils.contentEquals(inputStream, new ByteBufInputStream(buffer))); + } + } + + @Test + public void testReadByteByByte() throws Exception { + ResultCallbackTest callback = new ResultCallbackTest(); + HttpResponseStreamHandler streamHandler = new HttpResponseStreamHandler(callback); + ChannelHandlerContext ctx = Mockito.mock(ChannelHandlerContext.class); + ByteBuf buffer = generateByteBuf(); + streamHandler.channelRead0(ctx, buffer); + streamHandler.channelInactive(ctx); + + try (InputStream inputStream = callback.getInputStream()) { + for (int i = 0; i < buffer.readableBytes(); i++) { + int b = inputStream.read(); + assertEquals(b, buffer.getByte(i)); + } + assertTrue(inputStream.read() == -1); + } + } + + @Test + public void testCloseResponseStreamBeforeWrite() throws Exception { + HttpResponseInputStream inputStream = new HttpResponseInputStream(); + ByteBuf buffer = generateByteBuf(); + + inputStream.write(buffer); + inputStream.close(); + inputStream.write(buffer); + } + + @Test + public void testCloseResponseStreamOnWrite() throws Exception { + final HttpResponseInputStream inputStream = new HttpResponseInputStream(); + + final ByteBuf buffer = generateByteBuf(); + + final CountDownLatch firstWrite = new CountDownLatch(1); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future submit = executor.submit(new Runnable() { + @Override + public void run() { + try { + inputStream.write(buffer); + firstWrite.countDown(); + inputStream.write(buffer); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + + firstWrite.await(); + assertTrue(inputStream.available() > 0); + + // second write should have started + Thread.sleep(500L); + inputStream.close(); + + submit.get(); + } + + @Test(expectedExceptions = IOException.class) + public void testReadClosedResponseStream() throws Exception { + HttpResponseInputStream inputStream = new HttpResponseInputStream(); + ByteBuf buffer = generateByteBuf(); - assertTrue(IOUtils.contentEquals(callback.getInputStream(), new ByteBufInputStream(buffer))); + inputStream.write(buffer); + inputStream.close(); + inputStream.read(); } private ByteBuf generateByteBuf() { @@ -46,7 +126,7 @@ public void onNext(InputStream stream) { this.stream = stream; } - public InputStream getInputStream() { + private InputStream getInputStream() { return stream; } } diff --git a/src/test/java/com/github/dockerjava/utils/ContainerUtils.java b/src/test/java/com/github/dockerjava/utils/ContainerUtils.java new file mode 100644 index 000000000..206be0693 --- /dev/null +++ b/src/test/java/com/github/dockerjava/utils/ContainerUtils.java @@ -0,0 +1,70 @@ +package com.github.dockerjava.utils; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +/** + * Container cmd utils + */ +public class ContainerUtils { + + private ContainerUtils() { + } + + /** + * Starts container and ensures that it running + * + * @param dockerClient docker client + * @param container container + */ + public static void startContainer(DockerClient dockerClient, CreateContainerResponse container) { + dockerClient.startContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + assertThat(inspectContainerResponse.getState().getRunning(), is(true)); + } + + /** + * Pauses container and ensures that it paused + * + * @param dockerClient docker client + * @param container container + */ + public static void pauseContainer(DockerClient dockerClient, CreateContainerResponse container) { + dockerClient.pauseContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + assertThat(inspectContainerResponse.getState().getPaused(), is(true)); + } + + /** + * Stops container and ensures that it stopped + * + * @param dockerClient docker client + * @param container container + */ + public static void stopContainer(DockerClient dockerClient, CreateContainerResponse container) { + dockerClient.stopContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + assertThat(inspectContainerResponse.getState().getRunning(), is(false)); + } + + /** + * Unpauses container and ensures that it unpaused (running) + * + * @param dockerClient docker client + * @param container container + */ + public static void unpauseContainer(DockerClient dockerClient, CreateContainerResponse container) { + dockerClient.unpauseContainerCmd(container.getId()).exec(); + + InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec(); + assertThat(inspectContainerResponse.getState().getPaused(), is(false)); + assertThat(inspectContainerResponse.getState().getRunning(), is(true)); + } +} diff --git a/src/test/java/com/github/dockerjava/utils/TestResources.java b/src/test/java/com/github/dockerjava/utils/TestResources.java new file mode 100644 index 000000000..35ece680f --- /dev/null +++ b/src/test/java/com/github/dockerjava/utils/TestResources.java @@ -0,0 +1,14 @@ +package com.github.dockerjava.utils; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public class TestResources { + + private TestResources() { + } + + public static Path getApiImagesLoadTestTarball() { + return Paths.get("src/test/resources/api/images/load/image.tar"); + } +} diff --git a/src/test/resources/api/images/load/image.tar b/src/test/resources/api/images/load/image.tar new file mode 100644 index 000000000..6f2f08f9e Binary files /dev/null and b/src/test/resources/api/images/load/image.tar differ diff --git a/src/test/resources/testAddFile/Dockerfile b/src/test/resources/buildTests/ADD/file/Dockerfile similarity index 100% rename from src/test/resources/testAddFile/Dockerfile rename to src/test/resources/buildTests/ADD/file/Dockerfile diff --git a/src/test/resources/testAddFile/testrun.sh b/src/test/resources/buildTests/ADD/file/testrun.sh similarity index 100% rename from src/test/resources/testAddFile/testrun.sh rename to src/test/resources/buildTests/ADD/file/testrun.sh diff --git a/src/test/resources/testAddFileInSubfolder/Dockerfile b/src/test/resources/buildTests/ADD/fileInSubfolder/Dockerfile similarity index 100% rename from src/test/resources/testAddFileInSubfolder/Dockerfile rename to src/test/resources/buildTests/ADD/fileInSubfolder/Dockerfile diff --git a/src/test/resources/testAddFileInSubfolder/files/testrun.sh b/src/test/resources/buildTests/ADD/fileInSubfolder/files/testrun.sh similarity index 100% rename from src/test/resources/testAddFileInSubfolder/files/testrun.sh rename to src/test/resources/buildTests/ADD/fileInSubfolder/files/testrun.sh diff --git a/src/test/resources/testAddMultipleFiles/Dockerfile b/src/test/resources/buildTests/ADD/files/Dockerfile similarity index 100% rename from src/test/resources/testAddMultipleFiles/Dockerfile rename to src/test/resources/buildTests/ADD/files/Dockerfile diff --git a/src/test/resources/testAddMultipleFiles/src1 b/src/test/resources/buildTests/ADD/files/src1 similarity index 100% rename from src/test/resources/testAddMultipleFiles/src1 rename to src/test/resources/buildTests/ADD/files/src1 diff --git a/src/test/resources/testAddMultipleFiles/src2 b/src/test/resources/buildTests/ADD/files/src2 similarity index 100% rename from src/test/resources/testAddMultipleFiles/src2 rename to src/test/resources/buildTests/ADD/files/src2 diff --git a/src/test/resources/testAddFilesViaWildcard/Dockerfile b/src/test/resources/buildTests/ADD/filesViaWildcard/Dockerfile similarity index 100% rename from src/test/resources/testAddFilesViaWildcard/Dockerfile rename to src/test/resources/buildTests/ADD/filesViaWildcard/Dockerfile diff --git a/src/test/resources/testAddFilesViaWildcard/folder1/testrun.sh b/src/test/resources/buildTests/ADD/filesViaWildcard/folder1/testrun.sh similarity index 100% rename from src/test/resources/testAddFilesViaWildcard/folder1/testrun.sh rename to src/test/resources/buildTests/ADD/filesViaWildcard/folder1/testrun.sh diff --git a/src/test/resources/testAddFilesViaWildcard/folder2/testinclude1.sh b/src/test/resources/buildTests/ADD/filesViaWildcard/folder2/testinclude1.sh similarity index 100% rename from src/test/resources/testAddFilesViaWildcard/folder2/testinclude1.sh rename to src/test/resources/buildTests/ADD/filesViaWildcard/folder2/testinclude1.sh diff --git a/src/test/resources/testAddFilesViaWildcard/ignore/testinclude2.sh b/src/test/resources/buildTests/ADD/filesViaWildcard/ignore/testinclude2.sh similarity index 100% rename from src/test/resources/testAddFilesViaWildcard/ignore/testinclude2.sh rename to src/test/resources/buildTests/ADD/filesViaWildcard/ignore/testinclude2.sh diff --git a/src/test/resources/testAddFolder/Dockerfile b/src/test/resources/buildTests/ADD/folder/Dockerfile similarity index 100% rename from src/test/resources/testAddFolder/Dockerfile rename to src/test/resources/buildTests/ADD/folder/Dockerfile diff --git a/src/test/resources/testAddFolder/folderA/testAddFolder.sh b/src/test/resources/buildTests/ADD/folder/folderA/testAddFolder.sh similarity index 100% rename from src/test/resources/testAddFolder/folderA/testAddFolder.sh rename to src/test/resources/buildTests/ADD/folder/folderA/testAddFolder.sh diff --git a/src/test/resources/testAddUrl/Dockerfile b/src/test/resources/buildTests/ADD/url/Dockerfile similarity index 71% rename from src/test/resources/testAddUrl/Dockerfile rename to src/test/resources/buildTests/ADD/url/Dockerfile index d9a0676d4..2b10c34e5 100644 --- a/src/test/resources/testAddUrl/Dockerfile +++ b/src/test/resources/buildTests/ADD/url/Dockerfile @@ -2,7 +2,7 @@ FROM ubuntu:latest # Copy testrun.sh files into the container -ADD https://hub.docker.com/r/marcuslinke/busybox/ /tmp/docker_home.html +ADD http://www.example.com/index.html /tmp/some.html ADD ./testrun.sh /tmp/ RUN cp /tmp/testrun.sh /usr/local/bin/ && chmod +x /usr/local/bin/testrun.sh diff --git a/src/test/resources/buildTests/ADD/url/testrun.sh b/src/test/resources/buildTests/ADD/url/testrun.sh new file mode 100755 index 000000000..4d240d5ae --- /dev/null +++ b/src/test/resources/buildTests/ADD/url/testrun.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cat /tmp/some.html + diff --git a/src/test/resources/nginx/Dockerfile b/src/test/resources/buildTests/AUTHOR/Dockerfile similarity index 81% rename from src/test/resources/nginx/Dockerfile rename to src/test/resources/buildTests/AUTHOR/Dockerfile index 2d0fa24cb..32ffcedc7 100644 --- a/src/test/resources/nginx/Dockerfile +++ b/src/test/resources/buildTests/AUTHOR/Dockerfile @@ -9,4 +9,4 @@ MAINTAINER Guillaume J. Charmes "guillaume@dotcloud.com" 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 +RUN apt-get install -y nginx \ No newline at end of file diff --git a/src/test/resources/testENVSubstitution/Dockerfile b/src/test/resources/buildTests/ENV/Dockerfile similarity index 100% rename from src/test/resources/testENVSubstitution/Dockerfile rename to src/test/resources/buildTests/ENV/Dockerfile diff --git a/src/test/resources/testENVSubstitution/subst-file-2-abc123.txt b/src/test/resources/buildTests/ENV/subst-file-2-abc123.txt similarity index 100% rename from src/test/resources/testENVSubstitution/subst-file-2-abc123.txt rename to src/test/resources/buildTests/ENV/subst-file-2-abc123.txt diff --git a/src/test/resources/testENVSubstitution/subst-file-abc123.txt b/src/test/resources/buildTests/ENV/subst-file-abc123.txt similarity index 100% rename from src/test/resources/testENVSubstitution/subst-file-abc123.txt rename to src/test/resources/buildTests/ENV/subst-file-abc123.txt diff --git a/src/test/resources/testENVSubstitution/testrun.sh b/src/test/resources/buildTests/ENV/testrun.sh similarity index 100% rename from src/test/resources/testENVSubstitution/testrun.sh rename to src/test/resources/buildTests/ENV/testrun.sh diff --git a/src/test/resources/testBuildFromPrivateRegistry/Dockerfile b/src/test/resources/buildTests/FROM/privateRegistry/Dockerfile similarity index 100% rename from src/test/resources/testBuildFromPrivateRegistry/Dockerfile rename to src/test/resources/buildTests/FROM/privateRegistry/Dockerfile diff --git a/src/test/resources/testAddOnBuild/test/Dockerfile b/src/test/resources/buildTests/ONBUILD/child/Dockerfile similarity index 100% rename from src/test/resources/testAddOnBuild/test/Dockerfile rename to src/test/resources/buildTests/ONBUILD/child/Dockerfile diff --git a/src/test/resources/testAddOnBuild/test/testrun.sh b/src/test/resources/buildTests/ONBUILD/child/testrun.sh similarity index 100% rename from src/test/resources/testAddOnBuild/test/testrun.sh rename to src/test/resources/buildTests/ONBUILD/child/testrun.sh diff --git a/src/test/resources/testAddOnBuild/onbuild/Dockerfile b/src/test/resources/buildTests/ONBUILD/parent/Dockerfile similarity index 100% rename from src/test/resources/testAddOnBuild/onbuild/Dockerfile rename to src/test/resources/buildTests/ONBUILD/parent/Dockerfile diff --git a/src/test/resources/testBuildArgs/Dockerfile b/src/test/resources/buildTests/buildArgs/Dockerfile similarity index 100% rename from src/test/resources/testBuildArgs/Dockerfile rename to src/test/resources/buildTests/buildArgs/Dockerfile diff --git a/src/test/resources/testDockerfileNotInBaseDirectory/dockerfileFolder/Dockerfile b/src/test/resources/buildTests/dockerfileNotInBaseDirectory/dockerfileFolder/Dockerfile similarity index 100% rename from src/test/resources/testDockerfileNotInBaseDirectory/dockerfileFolder/Dockerfile rename to src/test/resources/buildTests/dockerfileNotInBaseDirectory/dockerfileFolder/Dockerfile diff --git a/src/test/resources/testDockerfileNotInBaseDirectory/testrunFolder/testrun.sh b/src/test/resources/buildTests/dockerfileNotInBaseDirectory/testrunFolder/testrun.sh similarity index 100% rename from src/test/resources/testDockerfileNotInBaseDirectory/testrunFolder/testrun.sh rename to src/test/resources/buildTests/dockerfileNotInBaseDirectory/testrunFolder/testrun.sh diff --git a/src/test/resources/testDockerfileIgnored/.dockerignore b/src/test/resources/buildTests/dockerignore/DockerfileIgnored/.dockerignore similarity index 100% rename from src/test/resources/testDockerfileIgnored/.dockerignore rename to src/test/resources/buildTests/dockerignore/DockerfileIgnored/.dockerignore diff --git a/src/test/resources/testDockerfileIgnored/Dockerfile b/src/test/resources/buildTests/dockerignore/DockerfileIgnored/Dockerfile similarity index 100% rename from src/test/resources/testDockerfileIgnored/Dockerfile rename to src/test/resources/buildTests/dockerignore/DockerfileIgnored/Dockerfile diff --git a/src/test/resources/testDockerfileIgnored/testrun.sh b/src/test/resources/buildTests/dockerignore/DockerfileIgnored/testrun.sh similarity index 100% rename from src/test/resources/testDockerfileIgnored/testrun.sh rename to src/test/resources/buildTests/dockerignore/DockerfileIgnored/testrun.sh diff --git a/src/test/resources/testDockerfileNotIgnored/.dockerignore b/src/test/resources/buildTests/dockerignore/DockerfileNotIgnored/.dockerignore similarity index 100% rename from src/test/resources/testDockerfileNotIgnored/.dockerignore rename to src/test/resources/buildTests/dockerignore/DockerfileNotIgnored/.dockerignore diff --git a/src/test/resources/testDockerfileNotIgnored/Dockerfile b/src/test/resources/buildTests/dockerignore/DockerfileNotIgnored/Dockerfile similarity index 100% rename from src/test/resources/testDockerfileNotIgnored/Dockerfile rename to src/test/resources/buildTests/dockerignore/DockerfileNotIgnored/Dockerfile diff --git a/src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/.dockerignore b/src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/.dockerignore new file mode 100644 index 000000000..f22e777b8 --- /dev/null +++ b/src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/.dockerignore @@ -0,0 +1,3 @@ +*.md +!README*.md +README-secret.md \ No newline at end of file diff --git a/src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/Dockerfile b/src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/Dockerfile new file mode 100644 index 000000000..65fa210c7 --- /dev/null +++ b/src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/Dockerfile @@ -0,0 +1,3 @@ +FROM ubuntu:latest + +CMD ["echo", "Success"] diff --git a/src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/README-secret.md b/src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/README-secret.md new file mode 100644 index 000000000..64237b5a7 --- /dev/null +++ b/src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/README-secret.md @@ -0,0 +1 @@ +No markdown files are included in the context except README files other than README-secret.md. \ No newline at end of file diff --git a/src/test/resources/testDockerignore/a/a b/src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/README.md similarity index 100% rename from src/test/resources/testDockerignore/a/a rename to src/test/resources/buildTests/dockerignore/EffectiveDockerignorePatterns/README.md diff --git a/src/test/resources/buildTests/dockerignore/IneffectiveDockerignorePattern/.dockerignore b/src/test/resources/buildTests/dockerignore/IneffectiveDockerignorePattern/.dockerignore new file mode 100644 index 000000000..f1a1f3799 --- /dev/null +++ b/src/test/resources/buildTests/dockerignore/IneffectiveDockerignorePattern/.dockerignore @@ -0,0 +1,3 @@ +*.md +README-secret.md +!README*.md diff --git a/src/test/resources/buildTests/dockerignore/IneffectiveDockerignorePattern/Dockerfile b/src/test/resources/buildTests/dockerignore/IneffectiveDockerignorePattern/Dockerfile new file mode 100644 index 000000000..65fa210c7 --- /dev/null +++ b/src/test/resources/buildTests/dockerignore/IneffectiveDockerignorePattern/Dockerfile @@ -0,0 +1,3 @@ +FROM ubuntu:latest + +CMD ["echo", "Success"] diff --git a/src/test/resources/testDockerignore/a/b b/src/test/resources/buildTests/dockerignore/IneffectiveDockerignorePattern/README-secret.md similarity index 100% rename from src/test/resources/testDockerignore/a/b rename to src/test/resources/buildTests/dockerignore/IneffectiveDockerignorePattern/README-secret.md diff --git a/src/test/resources/testDockerignore/a/c b/src/test/resources/buildTests/dockerignore/IneffectiveDockerignorePattern/README.md similarity index 100% rename from src/test/resources/testDockerignore/a/c rename to src/test/resources/buildTests/dockerignore/IneffectiveDockerignorePattern/README.md diff --git a/src/test/resources/testInvalidDockerignorePattern/.dockerignore b/src/test/resources/buildTests/dockerignore/InvalidDockerignorePattern/.dockerignore similarity index 100% rename from src/test/resources/testInvalidDockerignorePattern/.dockerignore rename to src/test/resources/buildTests/dockerignore/InvalidDockerignorePattern/.dockerignore diff --git a/src/test/resources/testInvalidDockerignorePattern/Dockerfile b/src/test/resources/buildTests/dockerignore/InvalidDockerignorePattern/Dockerfile similarity index 100% rename from src/test/resources/testInvalidDockerignorePattern/Dockerfile rename to src/test/resources/buildTests/dockerignore/InvalidDockerignorePattern/Dockerfile diff --git a/src/test/resources/testInvalidDockerignorePattern/testrun.sh b/src/test/resources/buildTests/dockerignore/InvalidDockerignorePattern/testrun.sh similarity index 100% rename from src/test/resources/testInvalidDockerignorePattern/testrun.sh rename to src/test/resources/buildTests/dockerignore/InvalidDockerignorePattern/testrun.sh diff --git a/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/.dockerignore b/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/.dockerignore new file mode 100644 index 000000000..d32df2a06 --- /dev/null +++ b/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/.dockerignore @@ -0,0 +1,5 @@ +**/*.txt +parent/*/grandChild/README*.md +parent/anotherChild/README*.md +!parent/anotherChild/*/*.md +!parent/child \ No newline at end of file diff --git a/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/Dockerfile b/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/Dockerfile new file mode 100644 index 000000000..65fa210c7 --- /dev/null +++ b/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/Dockerfile @@ -0,0 +1,3 @@ +FROM ubuntu:latest + +CMD ["echo", "Success"] diff --git a/src/test/resources/testDockerignore/a/d b/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/README.md similarity index 100% rename from src/test/resources/testDockerignore/a/d rename to src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/README.md diff --git a/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/a.txt b/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/a.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/anotherChild/README-child.md b/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/anotherChild/README-child.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/anotherChild/grandChild/README-grand.md b/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/anotherChild/grandChild/README-grand.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/anotherChild/grandChild/c.txt b/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/anotherChild/grandChild/c.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/child/b.txt b/src/test/resources/buildTests/dockerignore/NestedDirsDockerignore/parent/child/b.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/testDockerignore/.dockerignore b/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/.dockerignore similarity index 100% rename from src/test/resources/testDockerignore/.dockerignore rename to src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/.dockerignore diff --git a/src/test/resources/testDockerignore/Dockerfile b/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/Dockerfile similarity index 100% rename from src/test/resources/testDockerignore/Dockerfile rename to src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/Dockerfile diff --git a/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/a/a b/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/a/a new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/a/b b/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/a/b new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/a/c b/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/a/c new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/a/d b/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/a/d new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/testDockerignore/testrun.sh b/src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/testrun.sh similarity index 100% rename from src/test/resources/testDockerignore/testrun.sh rename to src/test/resources/buildTests/dockerignore/ValidDockerignorePattern/testrun.sh diff --git a/src/test/resources/buildTests/labels/Dockerfile b/src/test/resources/buildTests/labels/Dockerfile new file mode 100644 index 000000000..6a8106b86 --- /dev/null +++ b/src/test/resources/buildTests/labels/Dockerfile @@ -0,0 +1,3 @@ +FROM ubuntu:latest + +CMD ["echo", "Success"] diff --git a/src/test/resources/checkstyle/checkstyle-config.xml b/src/test/resources/checkstyle/checkstyle-config.xml index 33508c723..76671ded3 100644 --- a/src/test/resources/checkstyle/checkstyle-config.xml +++ b/src/test/resources/checkstyle/checkstyle-config.xml @@ -92,7 +92,9 @@ - + + + diff --git a/src/test/resources/netcat/Dockerfile b/src/test/resources/netcat/Dockerfile deleted file mode 100644 index 1974f1063..000000000 --- a/src/test/resources/netcat/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -# Firefox over VNC -# -# VERSION 0.3 - -FROM ubuntu:latest - -#install netcat -RUN apt-get install -y netcat - -EXPOSE 6900 -CMD ["nc", "-l", "6900"] \ No newline at end of file diff --git a/src/test/resources/nonstandard/subdirectory/Dockerfile-nonstandard b/src/test/resources/nonstandard/subdirectory/Dockerfile-nonstandard deleted file mode 100644 index 2d0fa24cb..000000000 --- a/src/test/resources/nonstandard/subdirectory/Dockerfile-nonstandard +++ /dev/null @@ -1,12 +0,0 @@ -# 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/samples/1.24/events/docs1.json b/src/test/resources/samples/1.24/events/docs1.json new file mode 100644 index 000000000..f62f2284e --- /dev/null +++ b/src/test/resources/samples/1.24/events/docs1.json @@ -0,0 +1,17 @@ +{ + "status": "create", + "id": "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743", + "from": "alpine", + "Type": "container", + "Action": "create", + "Actor": { + "ID": "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743", + "Attributes": { + "com.example.some-label": "some-label-value", + "image": "alpine", + "name": "my-container" + } + }, + "time": 1461943101, + "timeNano": 1461943101381709551 +} diff --git a/src/test/resources/testAddUrl/testrun.sh b/src/test/resources/testAddUrl/testrun.sh deleted file mode 100755 index 8eb4630b3..000000000 --- a/src/test/resources/testAddUrl/testrun.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -cat /tmp/docker_home.html - diff --git a/src/test/resources/testEnv/Dockerfile b/src/test/resources/testEnv/Dockerfile deleted file mode 100644 index a0a084633..000000000 --- a/src/test/resources/testEnv/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100755 index 14259aa77..000000000 --- a/src/test/resources/testEnv/subFolder/testrun.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -echo "Successfully executed testrun.sh" \ No newline at end of file diff --git a/src/test/resources/travis-logback.xml b/src/test/resources/travis-logback.xml new file mode 100644 index 000000000..250cbd335 --- /dev/null +++ b/src/test/resources/travis-logback.xml @@ -0,0 +1,19 @@ + + + + + %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + + + + + + + + +