From: Tianon Gravi Date: Wed, 4 Nov 2015 08:09:02 +0000 (+0000) Subject: docker.io (1.8.3~ds1-2) unstable; urgency=medium X-Git-Tag: archive/raspbian/18.09.1+dfsg1-7+rpi1~1^2^2^2~10 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=f20518c1186ddb72181a86976654383d73da0df3;p=docker.io.git docker.io (1.8.3~ds1-2) unstable; urgency=medium * Move "overlay" higher in priority (Closes: #799087) * Adjust "native.cgroupdriver" to default to "cgroupfs" (Closes: #798778) [dgit import unpatched docker.io 1.8.3~ds1-2] --- f20518c1186ddb72181a86976654383d73da0df3 diff --cc debian/Dockerfile index 00000000,00000000,00000000..80639c66 new file mode 100644 --- /dev/null +++ b/debian/Dockerfile @@@@ -1,0 -1,0 -1,0 +1,21 @@@@ +++FROM tianon/debian-devel +++ +++RUN echo 'deb http://incoming.debian.org/debian-buildd buildd-unstable main contrib non-free' > /etc/apt/sources.list.d/incoming.list +++ +++# start by adding just "debian/control" so we can get mk-build-deps with maximum caching +++COPY control /usr/src/docker.io/debian/ +++WORKDIR /usr/src/docker.io +++ +++# get all the build deps of _this_ package in a nice repeatable way +++RUN apt-get update && mk-build-deps -irt'apt-get --no-install-recommends -yV' debian/control && dpkg-checkbuilddeps +++ +++# need our debian/ directory to compile _this_ package +++COPY . /usr/src/docker.io/debian +++ +++# go download and unpack our upstream source +++RUN uscan --force-download --verbose --download-current-version +++RUN DOCKER_TARBALLS=.. ./debian/helpers/download-libcontainer +++RUN /tianon/extract-origtargz.sh +++ +++# tianon is _really_ lazy, and likes a preseeded bash history +++RUN echo '/tianon/extract-origtargz.sh && dpkg-buildpackage -us -uc && lintian -EvIL+pedantic' >> /root/.bash_history diff --cc debian/README.Debian index 00000000,00000000,00000000..2f41e350 new file mode 100644 --- /dev/null +++ b/debian/README.Debian @@@@ -1,0 -1,0 -1,0 +1,34 @@@@ +++Docker on Debian +++================ +++ +++To enable docker memory limitation, the kernel needs to be loaded with +++boot parameters: cgroup_enable=memory swapaccount=1. +++ +++This is because enabling memory cgroup support has some run-time overhead, +++and kernel maintainers don't want to slow down systems unnecessarily. +++ +++http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg764104.html +++https://github.com/docker/docker/issues/396 +++ +++To instruct the kernel to enable memory cgroup support, edit +++/etc/default/grub and extend GRUB_CMDLINE_LINUX_DEFAULT like: +++ +++GRUB_CMDLINE_LINUX_DEFAULT="cgroup_enable=memory swapaccount=1" +++ +++Then run update-grub, and reboot. +++ +++ +++As noted in the upstream documentation (https://docs.docker.io), Docker will +++allow non-root users in the "docker" group to access "docker.sock" and thus +++communicate with the daemon. To add yourself to the "docker" group, use +++something like: +++ +++adduser YOURUSER docker +++ +++As also noted in the upstream documentation, the "docker" group (and any other +++means of accessing the Docker API) is root-equivalent. If you don't trust a +++user with root on your box, you shouldn't trust them with Docker either. If you +++are interested in further information about the security aspects of Docker, +++please be sure to read the "Docker Security" +++(http://docs.docker.io/en/latest/articles/security/) article in the +++upstream documentation. diff --cc debian/changelog index 00000000,00000000,00000000..bd72cc54 new file mode 100644 --- /dev/null +++ b/debian/changelog @@@@ -1,0 -1,0 -1,0 +1,343 @@@@ +++docker.io (1.8.3~ds1-2) unstable; urgency=medium +++ +++ * Move "overlay" higher in priority (Closes: #799087) +++ * Adjust "native.cgroupdriver" to default to "cgroupfs" (Closes: #798778) +++ +++ -- Tianon Gravi Wed, 04 Nov 2015 00:09:02 -0800 +++ +++docker.io (1.8.3~ds1-1) unstable; urgency=medium +++ +++ * Update to 1.8.3 upstream release (CVE-2014-8178, CVE-2014-8179) +++ +++ -- Tianon Gravi Thu, 29 Oct 2015 19:40:51 -0700 +++ +++docker.io (1.8.2~ds1-2) unstable; urgency=medium +++ +++ * Swap Build-Depends order to appease buildds (Closes: #803136) +++ +++ -- Tianon Gravi Thu, 29 Oct 2015 07:23:10 -0700 +++ +++docker.io (1.8.2~ds1-1) unstable; urgency=medium +++ +++ * Update to 1.8.2 upstream release +++ * Rename golang-docker-dev package to golang-github-docker-docker-dev +++ * Add SELinux support (Closes: #799620) +++ +++ -- Tianon Gravi Wed, 28 Oct 2015 14:21:00 -0700 +++ +++docker.io (1.7.1~dfsg1-1) unstable; urgency=medium +++ +++ * Update to 1.7.1 upstream release +++ * Remove patches applied upstream; refresh other patches +++ * Update Build-Depends +++ +++ -- Tianon Gravi Wed, 26 Aug 2015 10:13:48 -0700 +++ +++docker.io (1.6.2~dfsg1-2) unstable; urgency=medium +++ +++ * Add DEP8 tests +++ - integration: runs upstream's integration tests +++ * Replace "code.google.com/p/go.net" with canonical "golang.org/x/net" +++ (Closes: #789736) +++ +++ -- Tianon Gravi Wed, 01 Jul 2015 07:45:19 -0600 +++ +++docker.io (1.6.2~dfsg1-1) unstable; urgency=medium +++ +++ * Update to 1.6.2 upstream release +++ * Update deps in d/control to match upstream's hack/vendor.sh specifications +++ +++ -- Tianon Gravi Thu, 21 May 2015 00:47:43 -0600 +++ +++docker.io (1.6.1+dfsg1-2) unstable; urgency=medium +++ +++ * Add --no-restart-on-upgrade to dh_installinit so that we don't force +++ a stop on upgrade, which can cause other units to fall over. Many thanks +++ to Michael Stapelberg (sECuRE) for the tip! +++ +++ -- Paul Tagliamonte Sun, 10 May 2015 13:02:54 -0400 +++ +++docker.io (1.6.1+dfsg1-1) unstable; urgency=high +++ +++ * Update to 1.6.1 upstream release (Closes: #784726) +++ - CVE-2015-3627 +++ Insecure opening of file-descriptor 1 leading to privilege escalation +++ - CVE-2015-3629 +++ Symlink traversal on container respawn allows local privilege escalation +++ - CVE-2015-3630 +++ Read/write proc paths allow host modification & information disclosure +++ - CVE-2015-3631 +++ Volume mounts allow LSM profile escalation +++ +++ -- Tianon Gravi Fri, 08 May 2015 17:57:10 -0600 +++ +++docker.io (1.6.0+dfsg1-1) unstable; urgency=medium +++ +++ * Upload to unstable +++ * Backport PR 12943 to support golang-go-patricia 2.* +++ * Remove convenience copies of cgroupfs-mount in init.d / upstart scripts +++ (Re: #783143) +++ +++ -- Tianon Gravi Tue, 05 May 2015 15:10:49 -0600 +++ +++docker.io (1.6.0+dfsg1-1~exp1) experimental; urgency=medium +++ +++ * Update to 1.6.0 upstream release +++ * Adjust "repack.sh" to be more tolerant of "dfsg" suffixes +++ +++ -- Tianon Gravi Thu, 16 Apr 2015 18:00:21 -0600 +++ +++docker.io (1.6.0~rc7~dfsg1-1~exp1) experimental; urgency=low +++ +++ * Update to 1.6.0-rc7 upstream release +++ +++ -- Tianon Gravi Wed, 15 Apr 2015 19:35:46 -0600 +++ +++docker.io (1.6.0~rc4~dfsg1-1) experimental; urgency=low +++ +++ [ Tianon Gravi ] +++ * Update to 1.6.0-rc4 upstream release +++ - drop golang 1.2 support (no longer supported upstream) +++ - update Homepage to https://dockerproject.com +++ - add check-config.sh to /usr/share/docker.io/contrib +++ - add "distribution" as a new multitarball orig +++ - backport auto "btrfs_noversion" patch from +++ https://github.com/docker/docker/pull/12048 +++ (simplifying our logic for detecting whether to use it) +++ - switch from dh-golang to direct install since we're not actually using the +++ features it offers (due to upstream's build system) +++ - enable "docker.service" on boot by default for restart policies to work +++ +++ [ Felipe Sateler ] +++ * Add Built-Using for glibc (Closes: #769351). +++ +++ -- Tianon Gravi Mon, 06 Apr 2015 17:11:33 -0600 +++ +++docker.io (1.5.0~dfsg1-1) experimental; urgency=low +++ +++ * Update to 1.5.0 upstream release (Closes: #773495) +++ * Remove several patches applied upstream! +++ - 9637-fix-nuke-bashism.patch +++ - enable-non-amd64-arches.patch +++ * Fix btrfs-tools handling to allow for building with btrfs-tools < 1.16.1 +++ +++ -- Tianon Gravi Tue, 10 Mar 2015 22:58:49 -0600 +++ +++docker.io (1.3.3~dfsg1-2) unstable; urgency=medium +++ +++ * Add fatal-error-old-kernels.patch to make Docker refuse to start on old, +++ unsupported kernels (Closes: #774376) +++ * Fix dh_auto_clean to clean up after the build properly, especially to avoid +++ FTBFS when built twice (Closes: #774482) +++ +++ -- Tianon Gravi Sat, 03 Jan 2015 00:11:47 -0700 +++ +++docker.io (1.3.3~dfsg1-1) unstable; urgency=medium +++ +++ [ Tianon Gravi ] +++ * Update to 1.3.3 upstream release (Closes: #772909) +++ - Fix for CVE-2014-9356 (Path traversal during processing of absolute +++ symlinks) +++ - Fix for CVE-2014-9357 (Escalation of privileges during decompression of +++ LZMA (.xz) archives) +++ - Fix for CVE-2014-9358 (Path traversal and spoofing opportunities presented +++ through image identifiers) +++ * Fix bashism in nuke-graph-directory.sh (Closes: #772261) +++ +++ [ Didier Roche ] +++ * Support starting systemd service without /etc/default/docker +++ (Closes: #770293) +++ +++ -- Tianon Gravi Thu, 18 Dec 2014 21:54:12 -0700 +++ +++docker.io (1.3.2~dfsg1-1) unstable; urgency=high +++ +++ * Severity is set to high due to the sensitive nature of the CVEs this +++ upload fixes. +++ * Update to 1.3.2 upstream release +++ - Fix for CVE-2014-6407 (Archive extraction host privilege escalation) +++ - Fix for CVE-2014-6408 (Security options applied to image could lead +++ to container escalation) +++ * Remove Daniel Mizyrycki from Uploaders. Thanks for all your work! +++ +++ -- Paul Tagliamonte Mon, 24 Nov 2014 19:14:28 -0500 +++ +++docker.io (1.3.1~dfsg1-2) unstable; urgency=medium +++ +++ * Remove deprecated /usr/bin/docker.io symlink +++ - added as a temporary shim in 1.0.0~dfsg1-1 (13 Jun 2014) +++ - unused by package-installed files in 1.2.0~dfsg1-1 (13 Sep 2014) +++ +++ -- Tianon Gravi Fri, 07 Nov 2014 13:11:34 -0700 +++ +++docker.io (1.3.1~dfsg1-1) unstable; urgency=high +++ +++ * Update to 1.3.1 upstream release +++ - fix for CVE-2014-5277 +++ - https://groups.google.com/d/topic/docker-user/oYm0i3xShJU/discussion +++ +++ -- Tianon Gravi Mon, 03 Nov 2014 08:26:29 -0700 +++ +++docker.io (1.3.0~dfsg1-1) unstable; urgency=medium +++ +++ * Updated to 1.3.0 upstream release. +++ * Enable systemd socket activation (Closes: #752555). +++ +++ -- Tianon Gravi Fri, 17 Oct 2014 00:56:07 -0600 +++ +++docker.io (1.2.0~dfsg1-2) unstable; urgency=medium +++ +++ * Added "golang-docker-dev" package for the reusable bits of Docker's source. +++ +++ -- Tianon Gravi Thu, 09 Oct 2014 00:08:11 +0000 +++ +++docker.io (1.2.0~dfsg1-1) unstable; urgency=medium +++ +++ * Updated to 1.2.0 upstream release (Closes: #757183, #757023, #757024). +++ * Added upstream man pages. +++ * Updated bash and zsh completions to be installed as "docker" and "_docker". +++ * Updated init scripts to also be installed as "docker". +++ * Fixed "equivalent" typo in README.Debian (Closes: #756395). Thanks Reuben! +++ * Removed "docker.io" mention in README.Debian (Closes: #756290). Thanks +++ Olivier! +++ +++ -- Tianon Gravi Sat, 13 Sep 2014 11:43:17 -0600 +++ +++docker.io (1.0.0~dfsg1-1) unstable; urgency=medium +++ +++ * Updated to 1.0.0 upstream release. Huzzah! +++ * I've removed what is commonly called a `button' of patches against +++ the docker package. Exact patches: +++ - bash-completion-docker.io.patch +++ - systemd-docker.io.patch +++ - sysvinit-provides-docker.io.patch +++ - zsh-completion-docker.io.patch +++ - mkimage-docker.io.patch +++ * I know y'all are guessing why; and the answer's pretty simple -- we're +++ no longer docker.io(1). Since the src:docker package now ships wmdocker(1), +++ we can safely declare a breaks/replaces on the pre-wmdocker version of the +++ package, allowing existing users to safely update, both src:docker and +++ src:docker.io side. This brings us into line with other distros, which +++ now ship wmdocker(1) and docker(1). +++ * As a stop-gap, I'm still shipping a docker.io(1) symlink to allow +++ migration away. +++ +++ -- Paul Tagliamonte Fri, 13 Jun 2014 21:04:53 -0400 +++ +++docker.io (0.11.1~dfsg1-1) unstable; urgency=medium +++ +++ [ Paul Tagliamonte ] +++ * Use EnvironmentFile with the systemd unit file. (Closes: #746774) +++ * Patch out version checking code. (Closes: #747140) +++ * Remove all host checking for non-amd64 host arches. Let docker build +++ and run on all platforms now. (Closes: #747139, #739914) +++ +++ [ Tianon Gravi ] +++ * Updated to 0.11.1 upstream release. +++ * Added backported upstream patch for removing RemoteAddr assumptions +++ that cause events to not be delivered to more than one unix socket +++ listener. +++ +++ -- Tianon Gravi Fri, 09 May 2014 17:30:45 -0400 +++ +++docker.io (0.9.1~dfsg1-2) unstable; urgency=medium +++ +++ * Added upstream apparmor patch to fix newer apparmor versions (such as the +++ version appearing in Ubuntu 14.04). +++ * Added mkimage-* docker.io binary name patches (Closes: #740855). +++ +++ -- Tianon Gravi Tue, 08 Apr 2014 23:19:08 -0400 +++ +++docker.io (0.9.1~dfsg1-1) unstable; urgency=medium +++ +++ * Updated to 0.9.1 upstream release (Closes: #743424). +++ * Added cgroupfs-mount dependency (Closes: #742641). +++ * Added Suggests entries for optional features, chiefly lxc (Closes: #742081). +++ * Added notes about "root-equivalence" to README.Debian (Closes: #742387). +++ +++ -- Tianon Gravi Thu, 03 Apr 2014 21:38:30 -0400 +++ +++docker.io (0.9.0+dfsg1-1) unstable; urgency=medium +++ +++ * Updated README.Debian to not be quite so outdated (Closes: #740850). +++ * Updated to 0.9.0 upstream release. +++ +++ -- Tianon Gravi Tue, 11 Mar 2014 22:24:31 -0400 +++ +++docker.io (0.8.1+dfsg1-1) unstable; urgency=medium +++ +++ * Updated to 0.8.1 upstream release. +++ +++ -- Tianon Gravi Tue, 25 Feb 2014 20:56:31 -0500 +++ +++docker.io (0.8.0+dfsg1-2) unstable; urgency=medium +++ +++ [ Tianon Gravi ] +++ * Added more license notes to debian/copyright (Closes: #738627). +++ +++ -- Tianon Gravi Sat, 15 Feb 2014 17:51:58 -0500 +++ +++docker.io (0.8.0+dfsg1-1) unstable; urgency=medium +++ +++ [ Prach Pongpanich ] +++ * Added zsh completion. +++ +++ [ Tianon Gravi ] +++ * Updated to 0.8.0 upstream release. +++ * Added vim syntax files in new vim-syntax-docker package. +++ * Added note about minimum recommended kernel version to Description. +++ * Added contrib/*-integration files in /usr/share/docker.io/contrib. +++ +++ -- Tianon Gravi Mon, 10 Feb 2014 20:41:10 -0500 +++ +++docker.io (0.7.6+dfsg1-1) unstable; urgency=medium +++ +++ [ Johan Euphrosine ] +++ * Updated to 0.7.6. +++ * Added dependency to gocapability. +++ * Clean patches. +++ +++ [ Tianon Gravi ] +++ * Added contrib/mk* scripts from upstream into /usr/share/docker.io/contrib +++ (Closes: #736068). +++ * Added upstream udev rules file to stop device-mapper devices and mounts from +++ appearing in desktop environments through udisks. +++ +++ -- Johan Euphrosine Wed, 22 Jan 2014 22:50:47 -0500 +++ +++docker.io (0.7.1+dfsg1-1) unstable; urgency=medium +++ +++ [ Prach Pongpanich ] +++ * Fixed "docker: command not found" errors while using bash tab completion +++ (Closes: #735372). +++ +++ [ Tianon Gravi ] +++ * Updated to 0.7.1 upstream release (while we wait for gocapability to be +++ packaged). +++ * Added xz-utils recommend which is required for decompressing certain images +++ from the index. +++ +++ -- Tianon Gravi Wed, 15 Jan 2014 20:22:34 -0500 +++ +++docker.io (0.6.7+dfsg1-3) unstable; urgency=medium +++ +++ * Fixed FTBFS on non-amd64 platforms by setting the correct GOPATH. +++ * Fixed issues with Docker finding a valid dockerinit (Closes: #734758). +++ * Added aufs-tools dependency. +++ +++ -- Tianon Gravi Thu, 09 Jan 2014 20:10:20 -0500 +++ +++docker.io (0.6.7+dfsg1-2) unstable; urgency=medium +++ +++ * Added iptables dependency required for Docker to start. +++ * Added ca-certificates recommend required for pulling from the index. +++ +++ -- Tianon Gravi Wed, 08 Jan 2014 19:14:02 -0500 +++ +++docker.io (0.6.7+dfsg1-1) unstable; urgency=medium +++ +++ * Initial release (Closes: #706060, #730569) +++ * Document missing licenses in the source tree. Bad, paultag. Thanks +++ alteholz. +++ +++ -- Paul Tagliamonte Tue, 07 Jan 2014 21:06:10 -0500 diff --cc debian/compat index 00000000,00000000,00000000..ec635144 new file mode 100644 --- /dev/null +++ b/debian/compat @@@@ -1,0 -1,0 -1,0 +1,1 @@@@ +++9 diff --cc debian/control index 00000000,00000000,00000000..b7e15ddb new file mode 100644 --- /dev/null +++ b/debian/control @@@@ -1,0 -1,0 -1,0 +1,109 @@@@ +++Source: docker.io +++Section: admin +++Priority: optional +++Maintainer: Paul Tagliamonte +++Uploaders: Docker Packaging Team , +++ Tianon Gravi , +++ Johan Euphrosine +++# partially generated via https://gist.github.com/tianon/92ebbd1793864b9586bc +++Build-Depends: bash-completion, +++ btrfs-tools, +++ debhelper (>=9), +++ dh-systemd, +++ go-md2man (>= 1.0.3~), +++ golang-context-dev (>= 0.0~git20140604~) | golang-github-gorilla-context-dev (>= 0.0~git20140604~), +++ golang-dbus-dev (>= 2~) | golang-github-godbus-dbus-dev (>= 2~), +++ golang-etcd-dev (>= 2.0.0~) | golang-github-coreos-go-etcd-dev (>= 2.0.0~), +++ golang-fsnotify-dev (>= 1.2.0~) | golang-gopkg-fsnotify.v1-dev (>= 1.2.0~), +++ golang-github-armon-go-metrics-dev (>= 0.0~git20150106~), +++ golang-github-coreos-go-systemd-dev (>= 2~), +++ golang-github-docker-distribution-dev (>= 0.0~git20150827~), +++ golang-github-docker-libkv-dev (>= 0.0~git20150619~), +++ golang-github-docker-libtrust-dev (>= 0.0~git20150526~), +++ golang-github-docker-notary-dev (>= 0.0~git20150801~), +++ golang-github-endophage-gotuf-dev (>= 0.0~git20150811~), +++ golang-github-fluent-fluent-logger-golang-dev (>= 1.0.0~), +++ golang-github-gorilla-mux-dev (>= 0.0~git20140926~), +++ golang-github-graylog2-go-gelf-dev (>= 0.0~git20150610~), +++ golang-github-hashicorp-go-msgpack-dev (>= 0.0~git20140221~), +++ golang-github-hashicorp-serf-dev (>= 0.0~git20150212~), +++ golang-github-mattn-go-sqlite3-dev (>= 0.0~git20150629~), +++ golang-github-opencontainers-runc-dev (>= 0.0.2.1~), +++ golang-github-samuel-go-zookeeper-dev (>= 0.0~git20150415~), +++ golang-github-sirupsen-logrus-dev (>= 0.8.2~), +++ golang-github-tent-canonical-json-go-dev (>= 0.0~git20130607~), +++ golang-github-vbatts-tar-split-dev (>= 0.9.6~), +++ golang-github-vishvananda-netlink-dev (>= 0.0~git20150815~), +++ golang-github-vishvananda-netns-dev (>= 0.0~git20150630~), +++ golang-go-patricia-dev (>= 2.1.0~) | golang-github-tchap-go-patricia-dev (>= 2.1.0~), +++ golang-go-zfs-dev (>= 2.1.1~) | golang-github-mistifyio-go-zfs-dev (>= 2.1.1~), +++ golang-gocapability-dev (>= 0.0~git20150506~) | golang-github-syndtr-gocapability-dev (>= 0.0~git20150506~), +++ golang-golang-x-net-dev (>= 0.0~git20150610~), +++ golang-goprotobuf-dev (>= 0.0~git20150325~) | golang-github-golang-protobuf-dev (>= 0.0~git20150325~), +++ golang-gosqlite-dev (>= 0.0~hg20130530~) | golang-code.google-p-gosqlite-dev (>= 0.0~hg20130530~), +++ golang-pty-dev (>= 0.0~git20150511~) | golang-github-kr-pty-dev (>= 0.0~git20150511~), +++ golang-toml-dev (>= 0.0~git20150127~) | golang-github-burntsushi-toml-dev (>= 0.0~git20150127~), +++ libapparmor-dev, +++ libdevmapper-dev (>= 2:1.02.68~) +++Standards-Version: 3.9.6 +++Homepage: https://dockerproject.com +++Vcs-Git: git://anonscm.debian.org/docker/docker.io.git +++Vcs-Browser: http://anonscm.debian.org/cgit/docker/docker.io.git +++ +++Package: docker.io +++Architecture: linux-any +++Depends: adduser, iptables, ${misc:Depends}, ${perl:Depends}, ${shlibs:Depends} +++Recommends: ca-certificates, +++ cgroupfs-mount | cgroup-lite, +++ git, +++ xz-utils, +++ ${apparmor:Recommends} +++Replaces: docker (<< 1.5~) +++Breaks: docker (<< 1.5~) +++Suggests: aufs-tools, btrfs-tools, debootstrap, lxc, rinse, zfs-fuse | zfsutils +++Built-Using: ${misc:Built-Using}, ${libc:Built-Using} +++Description: Linux container runtime +++ Docker complements kernel namespacing with a high-level API which operates at +++ the process level. It runs unix processes with strong guarantees of isolation +++ and repeatability across servers. +++ . +++ Docker is a great building block for automating distributed systems: +++ large-scale web deployments, database clusters, continuous deployment systems, +++ private PaaS, service-oriented architectures, etc. +++ . +++ This package contains the daemon and client. Using docker.io on non-amd64 hosts +++ is not supported at this time. Please be careful when using it on anything +++ besides amd64. +++ . +++ Also, note that kernel version 3.8 or above is required for proper operation of +++ the daemon process, and that any lower versions may have subtle and/or glaring +++ issues. +++ +++Package: vim-syntax-docker +++Architecture: all +++Depends: vim, ${misc:Depends} +++Recommends: vim-addon-manager +++Suggests: docker.io +++Description: Docker container engine - Vim highlighting syntax files +++ This package provides syntax files for the Vim editor for editing Dockerfiles +++ from the Docker container engine. +++ +++Package: golang-github-docker-docker-dev +++Architecture: all +++Depends: ${misc:Depends} +++Built-Using: ${misc:Built-Using} +++Replaces: golang-docker-dev (<< 1.8.2~ds1-1~) +++Breaks: golang-docker-dev (<< 1.8.2~ds1-1~) +++Provides: golang-docker-dev +++Description: Externally reusable Go packages included with Docker +++ These packages are intentionally developed by upstream in such a way that they +++ are reusable to projects outside Docker and only rely on each other or other +++ external dependencies to be built. +++ +++Package: golang-docker-dev +++Section: oldlibs +++Architecture: all +++Depends: golang-github-docker-docker-dev, ${misc:Depends} +++Description: Transitional package for golang-github-docker-docker-dev +++ This is a transitional package to ease upgrades to the +++ golang-github-docker-docker-dev package. It can safely be removed. diff --cc debian/copyright index 00000000,00000000,00000000..a00f78e2 new file mode 100644 --- /dev/null +++ b/debian/copyright @@@@ -1,0 -1,0 -1,0 +1,137 @@@@ +++Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +++Upstream-Name: Docker +++Upstream-Contact: Docker, Inc. +++Source: https://github.com/docker/docker +++ +++Files: * +++Copyright: 2012-2014 Docker, Inc. +++License: Apache-2.0 +++ +++Files: debian/* +++Copyright: 2013-2014 Daniel Mizyrycki +++ 2013-2014 Paul Tagliamonte +++ 2012-2014 Michael Stapelberg +++ 2013-2014 Tianon Gravi +++ 2013-2014 Johan Euphrosine +++ 2014 Prach Pongpanich +++License: Apache-2.0 +++ +++Files: contrib/init/openrc/docker.initd +++Copyright: 1999-2013 Gentoo Foundation +++License: GPL-2 +++ +++Files: contrib/syntax/vim/* +++Copyright: 2013 Honza Pokorny +++License: BSD-2-clause +++ +++Files: pkg/mflag/* +++Copyright: 2014 The Docker & Go Authors +++License: BSD-3-clause-Google +++ +++Files: contrib/completion/zsh/* +++Copyright: 2013-2014 Felix Riedel +++License: BSD-3-clause-Generic +++ +++License: Apache-2.0 +++ Licensed under the Apache License, Version 2.0 (the "License"); +++ you may not use this file except in compliance with the License. +++ You may obtain a copy of the License at +++ . +++ http://www.apache.org/licenses/LICENSE-2.0 +++ . +++ Unless required by applicable law or agreed to in writing, software +++ distributed under the License is distributed on an "AS IS" BASIS, +++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +++ See the License for the specific language governing permissions and +++ limitations under the License. +++ . +++ On Debian systems, the complete text of the Apache version 2.0 license +++ can be found in "/usr/share/common-licenses/Apache-2.0". +++ +++License: GPL-2 +++ This file is part of Buildbot. Buildbot is free software: you can +++ redistribute it and/or modify it under the terms of the GNU General Public +++ License as published by the Free Software Foundation, version 2. +++ . +++ This program is distributed in the hope that it will be useful, but WITHOUT +++ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +++ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +++ details. +++ . +++ You should have received a copy of the GNU General Public License along with +++ this program; if not, write to the Free Software Foundation, Inc., 51 +++ Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +++ . +++ On Debian systems, the complete text of the Apache version 2.0 license +++ can be found in "/usr/share/common-licenses/GPL-2". +++ +++License: BSD-2-clause +++ Redistribution and use in source and binary forms, with or without +++ modification, are permitted provided that the following conditions are met: +++ . +++ 1. Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ 2. Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in the +++ documentation and/or other materials provided with the distribution. +++ . +++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +++ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +++ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +++ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +++ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +++ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +++ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +++ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +++ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +++ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +++ +++License: BSD-3-clause-Google +++ Redistribution and use in source and binary forms, with or without +++ modification, are permitted provided that the following conditions are +++ met: +++ . +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above +++ copyright notice, this list of conditions and the following disclaimer +++ in the documentation and/or other materials provided with the +++ distribution. +++ * Neither the name of Google Inc. nor the names of its +++ contributors may be used to endorse or promote products derived from +++ this software without specific prior written permission. +++ . +++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +++ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +++ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +++ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +++ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +++ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +++ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +++ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +++ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +++ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +++ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +++ +++License: BSD-3-clause-Generic +++ Redistribution and use in source and binary forms, with or without +++ modification, are permitted provided that the following conditions are met: +++ * Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ * Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in the +++ documentation and/or other materials provided with the distribution. +++ * Neither the name of the nor the +++ names of its contributors may be used to endorse or promote products +++ derived from this software without specific prior written permission. +++ . +++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +++ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +++ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +++ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +++ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +++ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +++ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +++ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +++ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +++ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --cc debian/docker.io.bash-completion index 00000000,00000000,00000000..6ea11193 new file mode 100644 --- /dev/null +++ b/debian/docker.io.bash-completion @@@@ -1,0 -1,0 -1,0 +1,1 @@@@ +++contrib/completion/bash/docker diff --cc debian/docker.io.docker.default index 00000000,00000000,00000000..4312e355 new file mode 120000 --- /dev/null +++ b/debian/docker.io.docker.default @@@@ -1,0 -1,0 -1,0 +1,1 @@@@ +++../contrib/init/sysvinit-debian/docker.default diff --cc debian/docker.io.docker.init index 00000000,00000000,00000000..e27f0495 new file mode 120000 --- /dev/null +++ b/debian/docker.io.docker.init @@@@ -1,0 -1,0 -1,0 +1,1 @@@@ +++../contrib/init/sysvinit-debian/docker diff --cc debian/docker.io.docker.upstart index 00000000,00000000,00000000..4df3220e new file mode 120000 --- /dev/null +++ b/debian/docker.io.docker.upstart @@@@ -1,0 -1,0 -1,0 +1,1 @@@@ +++../contrib/init/upstart/docker.conf diff --cc debian/docker.io.install index 00000000,00000000,00000000..f9735ac7 new file mode 100644 --- /dev/null +++ b/debian/docker.io.install @@@@ -1,0 -1,0 -1,0 +1,7 @@@@ +++contrib/*-integration usr/share/docker.io/contrib/ +++contrib/check-config.sh usr/share/docker.io/contrib/ +++contrib/completion/zsh/_docker usr/share/zsh/vendor-completions/ +++contrib/init/systemd/docker.service lib/systemd/system/ +++contrib/init/systemd/docker.socket lib/systemd/system/ +++contrib/mk* usr/share/docker.io/contrib/ +++contrib/nuke-graph-directory.sh usr/share/docker.io/contrib/ diff --cc debian/docker.io.lintian-overrides index 00000000,00000000,00000000..37dde6ad new file mode 100644 --- /dev/null +++ b/debian/docker.io.lintian-overrides @@@@ -1,0 -1,0 -1,0 +1,2 @@@@ +++docker.io binary: statically-linked-binary usr/lib/docker.io/dockerinit +++# Yes, I assure you this is normal. Damnit, go. diff --cc debian/docker.io.maintscript index 00000000,00000000,00000000..adf69fc1 new file mode 100644 --- /dev/null +++ b/debian/docker.io.maintscript @@@@ -1,0 -1,0 -1,0 +1,4 @@@@ +++mv_conffile /etc/bash_completion.d/docker.io /etc/bash_completion.d/docker 1.2.0~ +++mv_conffile /etc/default/docker.io /etc/default/docker 1.2.0~ +++mv_conffile /etc/init.d/docker.io /etc/init.d/docker 1.2.0~ +++mv_conffile /etc/init/docker.io.conf /etc/init/docker.conf 1.2.0~ diff --cc debian/docker.io.manpages index 00000000,00000000,00000000..1aa62186 new file mode 100644 --- /dev/null +++ b/debian/docker.io.manpages @@@@ -1,0 -1,0 -1,0 +1,1 @@@@ +++man/man*/* diff --cc debian/docker.io.postinst index 00000000,00000000,00000000..5fd88472 new file mode 100644 --- /dev/null +++ b/debian/docker.io.postinst @@@@ -1,0 -1,0 -1,0 +1,18 @@@@ +++#!/bin/sh +++set -e +++ +++case "$1" in +++ configure) +++ if [ -z "$2" ]; then +++ addgroup --system docker +++ fi +++ ;; +++ abort-*) +++ # How'd we get here?? +++ exit 1 +++ ;; +++ *) +++ ;; +++esac +++ +++#DEBHELPER# diff --cc debian/docker.io.udev index 00000000,00000000,00000000..d7fd2eb6 new file mode 120000 --- /dev/null +++ b/debian/docker.io.udev @@@@ -1,0 -1,0 -1,0 +1,1 @@@@ +++../contrib/udev/80-docker.rules diff --cc debian/docs index 00000000,00000000,00000000..b43bf86b new file mode 100644 --- /dev/null +++ b/debian/docs @@@@ -1,0 -1,0 -1,0 +1,1 @@@@ +++README.md diff --cc debian/gbp.conf index 00000000,00000000,00000000..09ea3f17 new file mode 100644 --- /dev/null +++ b/debian/gbp.conf @@@@ -1,0 -1,0 -1,0 +1,13 @@@@ +++[DEFAULT] +++cleaner = fakeroot debian/rules clean +++pristine-tar = False +++ +++[git-buildpackage] +++export-dir = ../build-area/ +++tarball-dir = ../tarballs/ +++ +++[git-dch] +++id-length = 7 +++meta = True +++auto = True +++full = True diff --cc debian/helpers/download-libcontainer index 00000000,00000000,00000000..bd1cdc34 new file mode 100755 --- /dev/null +++ b/debian/helpers/download-libcontainer @@@@ -1,0 -1,0 -1,0 +1,60 @@@@ +++#!/bin/bash +++set -e +++ +++mkdir -p "${DOCKER_TARBALLS:=../tarballs}" +++ +++# MUST STAY IN SYNC WITH LIST IN "debian/rules" +++multiOrigGopkgs=( +++ github.com/docker/libnetwork +++) +++ +++pkg="$(dpkg-parsechangelog -SSource)" +++ver="$(dpkg-parsechangelog -SVersion)" +++origVer="${ver%-*}" # strip everything from the last dash +++origVer="$(echo "$origVer" | sed -r 's/^[0-9]+://')" # strip epoch +++upstreamVer="${origVer%%[+~]ds*}" +++origTarballPrefix="${DOCKER_TARBALLS}/${pkg}_${origVer}.orig" +++unprunedTarballPrefix="${DOCKER_TARBALLS}/${pkg}_${upstreamVer}.orig" +++ +++if command -v curl &> /dev/null; then +++ curl='curl -fsSL' +++elif command -v wget &> /dev/null; then +++ curl='wget -qO-' +++else +++ echo >&2 'error: missing "curl" or "wget" - install one or the other' +++ exit 1 +++fi +++ +++get_hack_vendor() { +++ for path in hack/vendor.sh project/vendor.sh; do +++ if [ -e "${origTarballPrefix}.tar.gz" ]; then +++ # if we have the main orig tarball handy, let's prefer that +++ if tar -xzOf "${origTarballPrefix}.tar.gz" --wildcards '*/'"$path"; then +++ return +++ fi +++ else +++ # but fall back to grabbing it raw from github otherwise +++ if $curl "https://raw.githubusercontent.com/docker/docker/v${upstreamVer}/$path"; then +++ return +++ fi +++ fi +++ done +++} +++ +++ret=0 +++for gopkg in "${multiOrigGopkgs[@]}"; do +++ commit="$(get_hack_vendor | awk '$1 == "clone" && $2 == "git" && $3 == "'"$gopkg"'" { print $4 }')" +++ if [ "$commit" ]; then +++ origTar="$unprunedTarballPrefix-$(basename "$gopkg").tar.gz" +++ $curl "https://$gopkg/archive/$commit.tar.gz" > "$origTar" +++ +++ echo "successfully fetched $origTar" +++ echo " (from $gopkg commit $commit)" +++ +++ "$(dirname "$(readlink -f "$BASH_SOURCE")")/../repack.sh" --upstream-version "$upstreamVer" "$origTar" +++ else +++ echo >&2 "error: cannot find $gopkg commit!" +++ ret=1 +++ fi +++done +++exit $ret diff --cc debian/helpers/gitcommit.sh index 00000000,00000000,00000000..3ca038d1 new file mode 100755 --- /dev/null +++ b/debian/helpers/gitcommit.sh @@@@ -1,0 -1,0 -1,0 +1,34 @@@@ +++#!/bin/bash +++set -e +++ +++uVersion="$1" +++dVersion="$2" +++ +++if [ -z "$uVersion" ]; then +++ uVersion="$(cat VERSION)" +++fi +++if [ -z "$dVersion" ]; then +++ dVersion="$(dpkg-parsechangelog --show-field Version)" +++fi +++ +++if [ "${uVersion%-dev}" = "$uVersion" ]; then +++ # this is a straight-up release! easy-peasy +++ exec awk -F ': ' '$1 == "'"$uVersion"'" { print $2 }' debian/upstream-version-gitcommits +++fi +++ +++# must be a nightly, so let's look for clues about what the git commit is +++ +++if git rev-parse &> /dev/null; then +++ # well, this will be easy ;) +++ exec git rev-parse --short HEAD +++fi +++ +++if [ "${dVersion#*+*+}" != "$dVersion" ]; then +++ # must be something like "1.1.2+10013+8c38a3d-1~utopic1" (nightly!) +++ commit="${dVersion#*+*+}" +++ commit="${commit%%-*}" +++ exec echo "$commit" +++fi +++ +++# unknown... +++echo >&2 'warning: unable to determine DOCKER_GITCOMMIT' diff --cc debian/patches/15404.patch index 00000000,00000000,00000000..7cda1f93 new file mode 100644 --- /dev/null +++ b/debian/patches/15404.patch @@@@ -1,0 -1,0 -1,0 +1,85 @@@@ +++From f83d05c3be3c3bcc84f6fa229504848ee8078321 Mon Sep 17 00:00:00 2001 +++From: Vincent Batts +++Date: Fri, 7 Aug 2015 10:18:20 -0400 +++Subject: [PATCH] devicemapper: fix zero-sized field access +++ +++Fixes: #15279 +++ +++Due to +++https://github.com/golang/go/commit/7904946eeb35faece61bbf6f5b3cc8be2f519c17 +++the devices field is dropped. +++ +++This solution works on go1.4 and go1.5 +++ +++Signed-off-by: Vincent Batts +++--- +++ daemon/graphdriver/devmapper/deviceset.go | 14 +++++++++----- +++ pkg/devicemapper/devmapper_wrapper.go | 18 +++++++++++++++--- +++ 2 files changed, 24 insertions(+), 8 deletions(-) +++ +++diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go +++index 6dddeb1..97e2032 100644 +++--- a/daemon/graphdriver/devmapper/deviceset.go ++++++ b/daemon/graphdriver/devmapper/deviceset.go +++@@ -1509,12 +1509,16 @@ func (devices *DeviceSet) deactivatePool() error { +++ if err != nil { +++ return err +++ } +++- if d, err := devicemapper.GetDeps(devname); err == nil { +++- // Access to more Debug output +++- logrus.Debugf("[devmapper] devicemapper.GetDeps() %s: %#v", devname, d) ++++ ++++ if devinfo.Exists == 0 { ++++ return nil +++ } +++- if devinfo.Exists != 0 { +++- return devicemapper.RemoveDevice(devname) ++++ if err := devicemapper.RemoveDevice(devname); err != nil { ++++ return err ++++ } ++++ ++++ if d, err := devicemapper.GetDeps(devname); err == nil { ++++ logrus.Warnf("[devmapper] device %s still has %d active dependents", devname, d.Count) +++ } +++ +++ return nil +++diff --git a/pkg/devicemapper/devmapper_wrapper.go b/pkg/devicemapper/devmapper_wrapper.go +++index 87c2003..44ca772 100644 +++--- a/pkg/devicemapper/devmapper_wrapper.go ++++++ b/pkg/devicemapper/devmapper_wrapper.go +++@@ -38,7 +38,10 @@ static void log_with_errno_init() +++ */ +++ import "C" +++ +++-import "unsafe" ++++import ( ++++ "reflect" ++++ "unsafe" ++++) +++ +++ type ( +++ CDmTask C.struct_dm_task +++@@ -184,12 +187,21 @@ func dmTaskGetDepsFct(task *CDmTask) *Deps { +++ if Cdeps == nil { +++ return nil +++ } ++++ ++++ // golang issue: https://github.com/golang/go/issues/11925 ++++ hdr := reflect.SliceHeader{ ++++ Data: uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(Cdeps)) + unsafe.Sizeof(*Cdeps))), ++++ Len: int(Cdeps.count), ++++ Cap: int(Cdeps.count), ++++ } ++++ devices := *(*[]C.uint64_t)(unsafe.Pointer(&hdr)) ++++ +++ deps := &Deps{ +++ Count: uint32(Cdeps.count), +++ Filler: uint32(Cdeps.filler), +++ } +++- for _, device := range Cdeps.device { +++- deps.Device = append(deps.Device, (uint64)(device)) ++++ for _, device := range devices { ++++ deps.Device = append(deps.Device, uint64(device)) +++ } +++ return deps +++ } diff --cc debian/patches/cgroupdriver-cgroupfs.patch index 00000000,00000000,00000000..97d08bac new file mode 100644 --- /dev/null +++ b/debian/patches/cgroupdriver-cgroupfs.patch @@@@ -1,0 -1,0 -1,0 +1,22 @@@@ +++Description: adjust "native.cgroupdriver" default value to always be "cgroupfs" irrespective of systemd since Docker cannot handle the new unified hierarchy +++Author: Tianon Gravi +++Bug: https://github.com/docker/docker/issues/16256 +++Bug-Debian: https://bugs.debian.org/798778 +++ +++diff --git a/daemon/execdriver/native/driver.go b/daemon/execdriver/native/driver.go +++index 94f200a..b09c86c 100644 +++--- a/daemon/execdriver/native/driver.go ++++++ b/daemon/execdriver/native/driver.go +++@@ -74,9 +74,9 @@ func NewDriver(root, initPath string, options []string) (*Driver, error) { +++ // this makes sure there are no breaking changes to people +++ // who upgrade from versions without native.cgroupdriver opt +++ cgm := libcontainer.Cgroupfs +++- if systemd.UseSystemd() { +++- cgm = libcontainer.SystemdCgroups +++- } ++++ //if systemd.UseSystemd() { ++++ // cgm = libcontainer.SystemdCgroups ++++ //} +++ +++ // parse the options +++ for _, option := range options { diff --cc debian/patches/cgroupfs-mount-convenience-copy.patch index 00000000,00000000,00000000..938a55c3 new file mode 100644 --- /dev/null +++ b/debian/patches/cgroupfs-mount-convenience-copy.patch @@@@ -1,0 -1,0 -1,0 +1,81 @@@@ +++Author: Tianon Gravi +++Description: remove convenience copies of cgroupfs-mount in init.d / upstart +++Forwarded: not-needed +++Bug-Debian: https://bugs.debian.org/783143 +++ +++diff --git a/contrib/init/sysvinit-debian/docker b/contrib/init/sysvinit-debian/docker +++index 11500a0..3ad9e2e 100755 +++--- a/contrib/init/sysvinit-debian/docker ++++++ b/contrib/init/sysvinit-debian/docker +++@@ -59,37 +59,12 @@ fail_unless_root() { +++ fi +++ } +++ +++-cgroupfs_mount() { +++- # see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount +++- if grep -v '^#' /etc/fstab | grep -q cgroup \ +++- || [ ! -e /proc/cgroups ] \ +++- || [ ! -d /sys/fs/cgroup ]; then +++- return +++- fi +++- if ! mountpoint -q /sys/fs/cgroup; then +++- mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup +++- fi +++- ( +++- cd /sys/fs/cgroup +++- for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do +++- mkdir -p $sys +++- if ! mountpoint -q $sys; then +++- if ! mount -n -t cgroup -o $sys cgroup $sys; then +++- rmdir $sys || true +++- fi +++- fi +++- done +++- ) +++-} +++- +++ case "$1" in +++ start) +++ check_init +++ +++ fail_unless_root +++ +++- cgroupfs_mount +++- +++ touch "$DOCKER_LOGFILE" +++ chgrp docker "$DOCKER_LOGFILE" +++ +++diff --git a/contrib/init/upstart/docker.conf b/contrib/init/upstart/docker.conf +++index ec50b35..75858f8 100644 +++--- a/contrib/init/upstart/docker.conf ++++++ b/contrib/init/upstart/docker.conf +++@@ -9,29 +9,6 @@ respawn +++ +++ kill timeout 20 +++ +++-pre-start script +++- # see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount +++- if grep -v '^#' /etc/fstab | grep -q cgroup \ +++- || [ ! -e /proc/cgroups ] \ +++- || [ ! -d /sys/fs/cgroup ]; then +++- exit 0 +++- fi +++- if ! mountpoint -q /sys/fs/cgroup; then +++- mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup +++- fi +++- ( +++- cd /sys/fs/cgroup +++- for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do +++- mkdir -p $sys +++- if ! mountpoint -q $sys; then +++- if ! mount -n -t cgroup -o $sys cgroup $sys; then +++- rmdir $sys || true +++- fi +++- fi +++- done +++- ) +++-end script +++- +++ script +++ # modify these in /etc/default/$UPSTART_JOB (/etc/default/docker) +++ DOCKER=/usr/bin/$UPSTART_JOB diff --cc debian/patches/change-system-unit-env-file.patch index 00000000,00000000,00000000..5503c4aa new file mode 100644 --- /dev/null +++ b/debian/patches/change-system-unit-env-file.patch @@@@ -1,0 -1,0 -1,0 +1,20 @@@@ +++Author: Paul R. Tagliamonte +++Last-Update: 2014-05-07 +++Description: Use EnvironmentFile with the systemd unit file. +++Bug-Debian: http://bugs.debian.org/746774 +++Forwarded: no +++ +++diff --git a/contrib/init/systemd/docker.service b/contrib/init/systemd/docker.service +++index f09c2d3..8370631 100644 +++--- a/contrib/init/systemd/docker.service ++++++ b/contrib/init/systemd/docker.service +++@@ -6,7 +6,8 @@ Requires=docker.socket +++ +++ [Service] +++ Type=notify +++-ExecStart=/usr/bin/docker daemon -H fd:// ++++EnvironmentFile=-/etc/default/docker ++++ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS +++ MountFlags=slave +++ LimitNOFILE=1048576 +++ LimitNPROC=1048576 diff --cc debian/patches/check-v1.patch index 00000000,00000000,00000000..e194cac6 new file mode 100644 --- /dev/null +++ b/debian/patches/check-v1.patch @@@@ -1,0 -1,0 -1,0 +1,1053 @@@@ +++diff --git a/integration-cli/check_test.go b/integration-cli/check_test.go +++index defa02e..f9e2c67 100644 +++--- a/integration-cli/check_test.go ++++++ b/integration-cli/check_test.go +++@@ -3,7 +3,7 @@ package main +++ import ( +++ "testing" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func Test(t *testing.T) { +++diff --git a/integration-cli/docker_api_attach_test.go b/integration-cli/docker_api_attach_test.go +++index 06a1c48..001631e 100644 +++--- a/integration-cli/docker_api_attach_test.go ++++++ b/integration-cli/docker_api_attach_test.go +++@@ -8,7 +8,7 @@ import ( +++ "strings" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ "golang.org/x/net/websocket" +++ ) +++ +++diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go +++index d8dc448..571af60 100644 +++--- a/integration-cli/docker_api_containers_test.go ++++++ b/integration-cli/docker_api_containers_test.go +++@@ -17,7 +17,7 @@ import ( +++ "github.com/docker/docker/api/types" +++ "github.com/docker/docker/pkg/stringid" +++ "github.com/docker/docker/runconfig" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestContainerApiGetAll(c *check.C) { +++diff --git a/integration-cli/docker_api_events_test.go b/integration-cli/docker_api_events_test.go +++index 8ae7bf7..9120aa0 100644 +++--- a/integration-cli/docker_api_events_test.go ++++++ b/integration-cli/docker_api_events_test.go +++@@ -4,7 +4,7 @@ import ( +++ "net/http" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestEventsApiEmptyOutput(c *check.C) { +++diff --git a/integration-cli/docker_api_exec_resize_test.go b/integration-cli/docker_api_exec_resize_test.go +++index 01061ca..817c734 100644 +++--- a/integration-cli/docker_api_exec_resize_test.go ++++++ b/integration-cli/docker_api_exec_resize_test.go +++@@ -4,7 +4,7 @@ import ( +++ "net/http" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestExecResizeApiHeightWidthNoInt(c *check.C) { +++diff --git a/integration-cli/docker_api_exec_test.go b/integration-cli/docker_api_exec_test.go +++index 3d99fe6..0446230 100644 +++--- a/integration-cli/docker_api_exec_test.go ++++++ b/integration-cli/docker_api_exec_test.go +++@@ -8,7 +8,7 @@ import ( +++ "fmt" +++ "net/http" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // Regression test for #9414 +++diff --git a/integration-cli/docker_api_images_test.go b/integration-cli/docker_api_images_test.go +++index 339a2f4..ab21d4b 100644 +++--- a/integration-cli/docker_api_images_test.go ++++++ b/integration-cli/docker_api_images_test.go +++@@ -7,7 +7,7 @@ import ( +++ "strings" +++ +++ "github.com/docker/docker/api/types" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestApiImagesFilter(c *check.C) { +++diff --git a/integration-cli/docker_api_info_test.go b/integration-cli/docker_api_info_test.go +++index 4084289..1616e0e 100644 +++--- a/integration-cli/docker_api_info_test.go ++++++ b/integration-cli/docker_api_info_test.go +++@@ -4,7 +4,7 @@ import ( +++ "net/http" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestInfoApi(c *check.C) { +++diff --git a/integration-cli/docker_api_inspect_test.go b/integration-cli/docker_api_inspect_test.go +++index 2287e7d..a57e687 100644 +++--- a/integration-cli/docker_api_inspect_test.go ++++++ b/integration-cli/docker_api_inspect_test.go +++@@ -6,7 +6,7 @@ import ( +++ "net/http" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestInspectApiContainerResponse(c *check.C) { +++diff --git a/integration-cli/docker_api_logs_test.go b/integration-cli/docker_api_logs_test.go +++index d478447..d22f363 100644 +++--- a/integration-cli/docker_api_logs_test.go ++++++ b/integration-cli/docker_api_logs_test.go +++@@ -8,7 +8,7 @@ import ( +++ "strings" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestLogsApiWithStdout(c *check.C) { +++diff --git a/integration-cli/docker_api_network_test.go b/integration-cli/docker_api_network_test.go +++index 44d2b31..2da958f 100644 +++--- a/integration-cli/docker_api_network_test.go ++++++ b/integration-cli/docker_api_network_test.go +++@@ -7,7 +7,7 @@ import ( +++ "fmt" +++ "net/http" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func isNetworkAvailable(c *check.C, name string) bool { +++diff --git a/integration-cli/docker_api_resize_test.go b/integration-cli/docker_api_resize_test.go +++index c7a577b..e421403 100644 +++--- a/integration-cli/docker_api_resize_test.go ++++++ b/integration-cli/docker_api_resize_test.go +++@@ -4,7 +4,7 @@ import ( +++ "net/http" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestResizeApiResponse(c *check.C) { +++diff --git a/integration-cli/docker_api_service_test.go b/integration-cli/docker_api_service_test.go +++index df07219..27b63c8 100644 +++--- a/integration-cli/docker_api_service_test.go ++++++ b/integration-cli/docker_api_service_test.go +++@@ -7,7 +7,7 @@ import ( +++ "fmt" +++ "net/http" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func isServiceAvailable(c *check.C, name string, network string) bool { +++diff --git a/integration-cli/docker_api_stats_test.go b/integration-cli/docker_api_stats_test.go +++index f019e00..d1868fd 100644 +++--- a/integration-cli/docker_api_stats_test.go ++++++ b/integration-cli/docker_api_stats_test.go +++@@ -10,7 +10,7 @@ import ( +++ "time" +++ +++ "github.com/docker/docker/api/types" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestCliStatsNoStreamGetCpu(c *check.C) { +++diff --git a/integration-cli/docker_api_test.go b/integration-cli/docker_api_test.go +++index 6cbf301..0572583 100644 +++--- a/integration-cli/docker_api_test.go ++++++ b/integration-cli/docker_api_test.go +++@@ -8,7 +8,7 @@ import ( +++ "time" +++ +++ "github.com/docker/docker/api" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestApiOptionsRoute(c *check.C) { +++diff --git a/integration-cli/docker_api_version_test.go b/integration-cli/docker_api_version_test.go +++index b756794..42fb872 100644 +++--- a/integration-cli/docker_api_version_test.go ++++++ b/integration-cli/docker_api_version_test.go +++@@ -6,7 +6,7 @@ import ( +++ +++ "github.com/docker/docker/api/types" +++ "github.com/docker/docker/autogen/dockerversion" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestGetVersion(c *check.C) { +++diff --git a/integration-cli/docker_cli_attach_test.go b/integration-cli/docker_cli_attach_test.go +++index f45f775..90e389c 100644 +++--- a/integration-cli/docker_cli_attach_test.go ++++++ b/integration-cli/docker_cli_attach_test.go +++@@ -9,7 +9,7 @@ import ( +++ "sync" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ const attachWait = 5 * time.Second +++diff --git a/integration-cli/docker_cli_attach_unix_test.go b/integration-cli/docker_cli_attach_unix_test.go +++index 9718dc0..c30ff45 100644 +++--- a/integration-cli/docker_cli_attach_unix_test.go ++++++ b/integration-cli/docker_cli_attach_unix_test.go +++@@ -9,7 +9,7 @@ import ( +++ "time" +++ +++ "github.com/docker/docker/pkg/stringid" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ "github.com/kr/pty" +++ ) +++ +++diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go +++index 6dc24df..ad56d5d 100644 +++--- a/integration-cli/docker_cli_build_test.go ++++++ b/integration-cli/docker_cli_build_test.go +++@@ -21,7 +21,7 @@ import ( +++ "github.com/docker/docker/builder/command" +++ "github.com/docker/docker/pkg/archive" +++ "github.com/docker/docker/pkg/stringutils" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestBuildJSONEmptyRun(c *check.C) { +++diff --git a/integration-cli/docker_cli_build_unix_test.go b/integration-cli/docker_cli_build_unix_test.go +++index fc7bd92..c246dbd 100644 +++--- a/integration-cli/docker_cli_build_unix_test.go ++++++ b/integration-cli/docker_cli_build_unix_test.go +++@@ -7,7 +7,7 @@ import ( +++ "strings" +++ +++ "github.com/docker/docker/pkg/ulimit" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) { +++diff --git a/integration-cli/docker_cli_by_digest_test.go b/integration-cli/docker_cli_by_digest_test.go +++index 71f8b1a..a53e3db 100644 +++--- a/integration-cli/docker_cli_by_digest_test.go ++++++ b/integration-cli/docker_cli_by_digest_test.go +++@@ -9,7 +9,7 @@ import ( +++ "github.com/docker/distribution/digest" +++ "github.com/docker/distribution/manifest" +++ "github.com/docker/docker/utils" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ var ( +++diff --git a/integration-cli/docker_cli_commit_test.go b/integration-cli/docker_cli_commit_test.go +++index 125b2e3..7e0d6f5 100644 +++--- a/integration-cli/docker_cli_commit_test.go ++++++ b/integration-cli/docker_cli_commit_test.go +++@@ -3,7 +3,7 @@ package main +++ import ( +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestCommitAfterContainerIsDone(c *check.C) { +++diff --git a/integration-cli/docker_cli_config_test.go b/integration-cli/docker_cli_config_test.go +++index 2b08f47..1b574fb 100644 +++--- a/integration-cli/docker_cli_config_test.go ++++++ b/integration-cli/docker_cli_config_test.go +++@@ -11,7 +11,7 @@ import ( +++ +++ "github.com/docker/docker/autogen/dockerversion" +++ "github.com/docker/docker/pkg/homedir" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestConfigHttpHeader(c *check.C) { +++diff --git a/integration-cli/docker_cli_cp_from_container_test.go b/integration-cli/docker_cli_cp_from_container_test.go +++index 945a34f..f6f3159 100644 +++--- a/integration-cli/docker_cli_cp_from_container_test.go ++++++ b/integration-cli/docker_cli_cp_from_container_test.go +++@@ -4,7 +4,7 @@ import ( +++ "os" +++ "path/filepath" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // docker cp CONTAINER:PATH LOCALPATH +++diff --git a/integration-cli/docker_cli_cp_test.go b/integration-cli/docker_cli_cp_test.go +++index 64ae0b5..94f6935 100644 +++--- a/integration-cli/docker_cli_cp_test.go ++++++ b/integration-cli/docker_cli_cp_test.go +++@@ -10,7 +10,7 @@ import ( +++ "path/filepath" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ const ( +++diff --git a/integration-cli/docker_cli_cp_to_container_test.go b/integration-cli/docker_cli_cp_to_container_test.go +++index 341121d..a705ad1 100644 +++--- a/integration-cli/docker_cli_cp_to_container_test.go ++++++ b/integration-cli/docker_cli_cp_to_container_test.go +++@@ -3,7 +3,7 @@ package main +++ import ( +++ "os" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // docker cp LOCALPATH CONTAINER:PATH +++diff --git a/integration-cli/docker_cli_cp_utils.go b/integration-cli/docker_cli_cp_utils.go +++index c26ebfd..9ed8a7d 100644 +++--- a/integration-cli/docker_cli_cp_utils.go ++++++ b/integration-cli/docker_cli_cp_utils.go +++@@ -10,7 +10,7 @@ import ( +++ "strings" +++ +++ "github.com/docker/docker/pkg/archive" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ type fileType uint32 +++diff --git a/integration-cli/docker_cli_create_test.go b/integration-cli/docker_cli_create_test.go +++index 482e96f..9a0ad7e 100644 +++--- a/integration-cli/docker_cli_create_test.go ++++++ b/integration-cli/docker_cli_create_test.go +++@@ -13,7 +13,7 @@ import ( +++ "io/ioutil" +++ +++ "github.com/docker/docker/pkg/nat" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // Make sure we can create a simple container with some args +++diff --git a/integration-cli/docker_cli_daemon_experimental_test.go b/integration-cli/docker_cli_daemon_experimental_test.go +++index dc4f792..51c51d2 100644 +++--- a/integration-cli/docker_cli_daemon_experimental_test.go ++++++ b/integration-cli/docker_cli_daemon_experimental_test.go +++@@ -6,7 +6,7 @@ import ( +++ "os/exec" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func assertNetwork(c *check.C, d *Daemon, name string) { +++diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go +++index 992cd83..2dec0ec 100644 +++--- a/integration-cli/docker_cli_daemon_test.go ++++++ b/integration-cli/docker_cli_daemon_test.go +++@@ -17,7 +17,7 @@ import ( +++ +++ "github.com/docker/libnetwork/iptables" +++ "github.com/docker/libtrust" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerDaemonSuite) TestDaemonRestartWithRunningContainersPorts(c *check.C) { +++diff --git a/integration-cli/docker_cli_diff_test.go b/integration-cli/docker_cli_diff_test.go +++index b5fc1bd..4041620 100644 +++--- a/integration-cli/docker_cli_diff_test.go ++++++ b/integration-cli/docker_cli_diff_test.go +++@@ -3,7 +3,7 @@ package main +++ import ( +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // ensure that an added file shows up in docker diff +++diff --git a/integration-cli/docker_cli_events_test.go b/integration-cli/docker_cli_events_test.go +++index 6742ea4..2bcbfcd 100644 +++--- a/integration-cli/docker_cli_events_test.go ++++++ b/integration-cli/docker_cli_events_test.go +++@@ -13,7 +13,7 @@ import ( +++ "sync" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestEventsTimestampFormats(c *check.C) { +++diff --git a/integration-cli/docker_cli_events_unix_test.go b/integration-cli/docker_cli_events_unix_test.go +++index 1a08f2b..fd6dcd6 100644 +++--- a/integration-cli/docker_cli_events_unix_test.go ++++++ b/integration-cli/docker_cli_events_unix_test.go +++@@ -10,7 +10,7 @@ import ( +++ "os/exec" +++ "unicode" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ "github.com/kr/pty" +++ ) +++ +++diff --git a/integration-cli/docker_cli_exec_test.go b/integration-cli/docker_cli_exec_test.go +++index 8e85988..a626a7d 100644 +++--- a/integration-cli/docker_cli_exec_test.go ++++++ b/integration-cli/docker_cli_exec_test.go +++@@ -15,7 +15,7 @@ import ( +++ "sync" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestExec(c *check.C) { +++diff --git a/integration-cli/docker_cli_exec_unix_test.go b/integration-cli/docker_cli_exec_unix_test.go +++index 28c202c..fa0b274 100644 +++--- a/integration-cli/docker_cli_exec_unix_test.go ++++++ b/integration-cli/docker_cli_exec_unix_test.go +++@@ -9,7 +9,7 @@ import ( +++ "strings" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ "github.com/kr/pty" +++ ) +++ +++diff --git a/integration-cli/docker_cli_experimental_test.go b/integration-cli/docker_cli_experimental_test.go +++index 694222b..100b6c4 100644 +++--- a/integration-cli/docker_cli_experimental_test.go ++++++ b/integration-cli/docker_cli_experimental_test.go +++@@ -5,7 +5,7 @@ package main +++ import ( +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestExperimentalVersion(c *check.C) { +++diff --git a/integration-cli/docker_cli_export_import_test.go b/integration-cli/docker_cli_export_import_test.go +++index a9e75de..f227755 100644 +++--- a/integration-cli/docker_cli_export_import_test.go ++++++ b/integration-cli/docker_cli_export_import_test.go +++@@ -5,7 +5,7 @@ import ( +++ "os/exec" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // export an image and try to import it into a new one +++diff --git a/integration-cli/docker_cli_help_test.go b/integration-cli/docker_cli_help_test.go +++index 3113083..2c83671 100644 +++--- a/integration-cli/docker_cli_help_test.go ++++++ b/integration-cli/docker_cli_help_test.go +++@@ -8,7 +8,7 @@ import ( +++ "unicode" +++ +++ "github.com/docker/docker/pkg/homedir" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestHelpTextVerify(c *check.C) { +++diff --git a/integration-cli/docker_cli_history_test.go b/integration-cli/docker_cli_history_test.go +++index 355e4c8..7faac52 100644 +++--- a/integration-cli/docker_cli_history_test.go ++++++ b/integration-cli/docker_cli_history_test.go +++@@ -6,7 +6,7 @@ import ( +++ "strconv" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // This is a heisen-test. Because the created timestamp of images and the behavior of +++diff --git a/integration-cli/docker_cli_images_test.go b/integration-cli/docker_cli_images_test.go +++index fe12f02..1bc04ef 100644 +++--- a/integration-cli/docker_cli_images_test.go ++++++ b/integration-cli/docker_cli_images_test.go +++@@ -8,7 +8,7 @@ import ( +++ "time" +++ +++ "github.com/docker/docker/pkg/stringid" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestImagesEnsureImageIsListed(c *check.C) { +++diff --git a/integration-cli/docker_cli_import_test.go b/integration-cli/docker_cli_import_test.go +++index ccfc452..5d25068 100644 +++--- a/integration-cli/docker_cli_import_test.go ++++++ b/integration-cli/docker_cli_import_test.go +++@@ -7,7 +7,7 @@ import ( +++ "os/exec" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestImportDisplay(c *check.C) { +++diff --git a/integration-cli/docker_cli_info_test.go b/integration-cli/docker_cli_info_test.go +++index 86719f4..134c5c6 100644 +++--- a/integration-cli/docker_cli_info_test.go ++++++ b/integration-cli/docker_cli_info_test.go +++@@ -4,7 +4,7 @@ import ( +++ "strings" +++ +++ "github.com/docker/docker/utils" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // ensure docker info succeeds +++diff --git a/integration-cli/docker_cli_inspect_experimental_test.go b/integration-cli/docker_cli_inspect_experimental_test.go +++index fada86e..4c4f98b 100644 +++--- a/integration-cli/docker_cli_inspect_experimental_test.go ++++++ b/integration-cli/docker_cli_inspect_experimental_test.go +++@@ -4,7 +4,7 @@ package main +++ +++ import ( +++ "github.com/docker/docker/api/types" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestInspectNamedMountPoint(c *check.C) { +++diff --git a/integration-cli/docker_cli_inspect_test.go b/integration-cli/docker_cli_inspect_test.go +++index 3e42d0c..c52c2c8 100644 +++--- a/integration-cli/docker_cli_inspect_test.go ++++++ b/integration-cli/docker_cli_inspect_test.go +++@@ -8,7 +8,7 @@ import ( +++ "time" +++ +++ "github.com/docker/docker/api/types" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestInspectImage(c *check.C) { +++diff --git a/integration-cli/docker_cli_kill_test.go b/integration-cli/docker_cli_kill_test.go +++index 685f4f5..a3989b0 100644 +++--- a/integration-cli/docker_cli_kill_test.go ++++++ b/integration-cli/docker_cli_kill_test.go +++@@ -5,7 +5,7 @@ import ( +++ "net/http" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestKillContainer(c *check.C) { +++diff --git a/integration-cli/docker_cli_links_test.go b/integration-cli/docker_cli_links_test.go +++index 568f20a..e76f83d 100644 +++--- a/integration-cli/docker_cli_links_test.go ++++++ b/integration-cli/docker_cli_links_test.go +++@@ -2,7 +2,7 @@ package main +++ +++ import ( +++ "fmt" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ "reflect" +++ "regexp" +++ "strings" +++diff --git a/integration-cli/docker_cli_links_unix_test.go b/integration-cli/docker_cli_links_unix_test.go +++index 67a4464..bb886df 100644 +++--- a/integration-cli/docker_cli_links_unix_test.go ++++++ b/integration-cli/docker_cli_links_unix_test.go +++@@ -7,7 +7,7 @@ import ( +++ "os" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestLinksEtcHostsRegularFile(c *check.C) { +++diff --git a/integration-cli/docker_cli_login_test.go b/integration-cli/docker_cli_login_test.go +++index 3b4431d..409a007 100644 +++--- a/integration-cli/docker_cli_login_test.go ++++++ b/integration-cli/docker_cli_login_test.go +++@@ -4,7 +4,7 @@ import ( +++ "bytes" +++ "os/exec" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestLoginWithoutTTY(c *check.C) { +++diff --git a/integration-cli/docker_cli_logs_test.go b/integration-cli/docker_cli_logs_test.go +++index 6c94217..076e227 100644 +++--- a/integration-cli/docker_cli_logs_test.go ++++++ b/integration-cli/docker_cli_logs_test.go +++@@ -11,7 +11,7 @@ import ( +++ "time" +++ +++ "github.com/docker/docker/pkg/timeutils" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // This used to work, it test a log of PageSize-1 (gh#4851) +++diff --git a/integration-cli/docker_cli_nat_test.go b/integration-cli/docker_cli_nat_test.go +++index a0773fe..d1044f5 100644 +++--- a/integration-cli/docker_cli_nat_test.go ++++++ b/integration-cli/docker_cli_nat_test.go +++@@ -6,7 +6,7 @@ import ( +++ "net" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func startServerContainer(c *check.C, msg string, port int) string { +++diff --git a/integration-cli/docker_cli_network_test.go b/integration-cli/docker_cli_network_test.go +++index 08b225d..d931add 100644 +++--- a/integration-cli/docker_cli_network_test.go ++++++ b/integration-cli/docker_cli_network_test.go +++@@ -5,7 +5,7 @@ package main +++ import ( +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func assertNwIsAvailable(c *check.C, name string) { +++diff --git a/integration-cli/docker_cli_pause_test.go b/integration-cli/docker_cli_pause_test.go +++index 4e32dfc..72c9b4c 100644 +++--- a/integration-cli/docker_cli_pause_test.go ++++++ b/integration-cli/docker_cli_pause_test.go +++@@ -4,7 +4,7 @@ import ( +++ "fmt" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestPause(c *check.C) { +++diff --git a/integration-cli/docker_cli_port_test.go b/integration-cli/docker_cli_port_test.go +++index 63bfc9a..4eb5d44 100644 +++--- a/integration-cli/docker_cli_port_test.go ++++++ b/integration-cli/docker_cli_port_test.go +++@@ -6,7 +6,7 @@ import ( +++ "sort" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestPortList(c *check.C) { +++diff --git a/integration-cli/docker_cli_port_unix_test.go b/integration-cli/docker_cli_port_unix_test.go +++index 0988ca9..4cbf0ee 100644 +++--- a/integration-cli/docker_cli_port_unix_test.go ++++++ b/integration-cli/docker_cli_port_unix_test.go +++@@ -6,7 +6,7 @@ import ( +++ "net" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestPortHostBinding(c *check.C) { +++diff --git a/integration-cli/docker_cli_proxy_test.go b/integration-cli/docker_cli_proxy_test.go +++index 8b55c67..45737f0 100644 +++--- a/integration-cli/docker_cli_proxy_test.go ++++++ b/integration-cli/docker_cli_proxy_test.go +++@@ -5,7 +5,7 @@ import ( +++ "os/exec" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestCliProxyDisableProxyUnixSock(c *check.C) { +++diff --git a/integration-cli/docker_cli_ps_test.go b/integration-cli/docker_cli_ps_test.go +++index 3b8f6f9..25a2e87 100644 +++--- a/integration-cli/docker_cli_ps_test.go ++++++ b/integration-cli/docker_cli_ps_test.go +++@@ -11,7 +11,7 @@ import ( +++ "strings" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestPsListContainers(c *check.C) { +++diff --git a/integration-cli/docker_cli_pull_test.go b/integration-cli/docker_cli_pull_test.go +++index 8bfca8d..581366b 100644 +++--- a/integration-cli/docker_cli_pull_test.go ++++++ b/integration-cli/docker_cli_pull_test.go +++@@ -11,7 +11,7 @@ import ( +++ "time" +++ +++ "github.com/docker/distribution/digest" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // See issue docker/docker#8141 +++diff --git a/integration-cli/docker_cli_push_test.go b/integration-cli/docker_cli_push_test.go +++index 111e9f3..a087258 100644 +++--- a/integration-cli/docker_cli_push_test.go ++++++ b/integration-cli/docker_cli_push_test.go +++@@ -9,7 +9,7 @@ import ( +++ "strings" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // Pushing an image to a private registry. +++diff --git a/integration-cli/docker_cli_rename_test.go b/integration-cli/docker_cli_rename_test.go +++index cac9f3a..3ecc814 100644 +++--- a/integration-cli/docker_cli_rename_test.go ++++++ b/integration-cli/docker_cli_rename_test.go +++@@ -4,7 +4,7 @@ import ( +++ "strings" +++ +++ "github.com/docker/docker/pkg/stringid" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestRenameStoppedContainer(c *check.C) { +++diff --git a/integration-cli/docker_cli_restart_test.go b/integration-cli/docker_cli_restart_test.go +++index 4cabeb9..c558c3d 100644 +++--- a/integration-cli/docker_cli_restart_test.go ++++++ b/integration-cli/docker_cli_restart_test.go +++@@ -4,7 +4,7 @@ import ( +++ "strings" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestRestartStoppedContainer(c *check.C) { +++diff --git a/integration-cli/docker_cli_rm_test.go b/integration-cli/docker_cli_rm_test.go +++index 0e57551..69104bf 100644 +++--- a/integration-cli/docker_cli_rm_test.go ++++++ b/integration-cli/docker_cli_rm_test.go +++@@ -4,7 +4,7 @@ import ( +++ "os" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestRmContainerWithRemovedVolume(c *check.C) { +++diff --git a/integration-cli/docker_cli_rmi_test.go b/integration-cli/docker_cli_rmi_test.go +++index 8d9f94f..48faf9c 100644 +++--- a/integration-cli/docker_cli_rmi_test.go ++++++ b/integration-cli/docker_cli_rmi_test.go +++@@ -5,7 +5,7 @@ import ( +++ "os/exec" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestRmiWithContainerFails(c *check.C) { +++diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go +++index 9288957..a04d03f 100644 +++--- a/integration-cli/docker_cli_run_test.go ++++++ b/integration-cli/docker_cli_run_test.go +++@@ -20,7 +20,7 @@ import ( +++ +++ "github.com/docker/docker/pkg/nat" +++ "github.com/docker/libnetwork/resolvconf" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // "test123" should be printed by docker run +++diff --git a/integration-cli/docker_cli_run_unix_test.go b/integration-cli/docker_cli_run_unix_test.go +++index 295bace..140b2ba 100644 +++--- a/integration-cli/docker_cli_run_unix_test.go ++++++ b/integration-cli/docker_cli_run_unix_test.go +++@@ -14,7 +14,7 @@ import ( +++ "time" +++ +++ "github.com/docker/docker/pkg/mount" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ "github.com/kr/pty" +++ ) +++ +++diff --git a/integration-cli/docker_cli_save_load_test.go b/integration-cli/docker_cli_save_load_test.go +++index 5b4b64d..eb0d63f 100644 +++--- a/integration-cli/docker_cli_save_load_test.go ++++++ b/integration-cli/docker_cli_save_load_test.go +++@@ -10,7 +10,7 @@ import ( +++ "sort" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // save a repo using gz compression and try to load it using stdout +++diff --git a/integration-cli/docker_cli_save_load_unix_test.go b/integration-cli/docker_cli_save_load_unix_test.go +++index 2bca3b8..991b940 100644 +++--- a/integration-cli/docker_cli_save_load_unix_test.go ++++++ b/integration-cli/docker_cli_save_load_unix_test.go +++@@ -8,7 +8,7 @@ import ( +++ "os" +++ "os/exec" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ "github.com/kr/pty" +++ ) +++ +++diff --git a/integration-cli/docker_cli_search_test.go b/integration-cli/docker_cli_search_test.go +++index d89c05c..40db0df 100644 +++--- a/integration-cli/docker_cli_search_test.go ++++++ b/integration-cli/docker_cli_search_test.go +++@@ -3,7 +3,7 @@ package main +++ import ( +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // search for repos named "registry" on the central registry +++diff --git a/integration-cli/docker_cli_service_test.go b/integration-cli/docker_cli_service_test.go +++index aaf5e81..91b7b8b 100644 +++--- a/integration-cli/docker_cli_service_test.go ++++++ b/integration-cli/docker_cli_service_test.go +++@@ -6,7 +6,7 @@ import ( +++ "fmt" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func assertSrvIsAvailable(c *check.C, sname, name string) { +++diff --git a/integration-cli/docker_cli_start_test.go b/integration-cli/docker_cli_start_test.go +++index ce5c48e..8027735 100644 +++--- a/integration-cli/docker_cli_start_test.go ++++++ b/integration-cli/docker_cli_start_test.go +++@@ -5,7 +5,7 @@ import ( +++ "strings" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // Regression test for https://github.com/docker/docker/issues/7843 +++diff --git a/integration-cli/docker_cli_start_volume_driver_unix_test.go b/integration-cli/docker_cli_start_volume_driver_unix_test.go +++index 71f9f2d..1fddee0 100644 +++--- a/integration-cli/docker_cli_start_volume_driver_unix_test.go ++++++ b/integration-cli/docker_cli_start_volume_driver_unix_test.go +++@@ -12,7 +12,7 @@ import ( +++ "path/filepath" +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func init() { +++diff --git a/integration-cli/docker_cli_stats_test.go b/integration-cli/docker_cli_stats_test.go +++index 2504028..7cd6367 100644 +++--- a/integration-cli/docker_cli_stats_test.go ++++++ b/integration-cli/docker_cli_stats_test.go +++@@ -5,7 +5,7 @@ import ( +++ "strings" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestCliStatsNoStream(c *check.C) { +++diff --git a/integration-cli/docker_cli_tag_test.go b/integration-cli/docker_cli_tag_test.go +++index 23f2aef..109dae3 100644 +++--- a/integration-cli/docker_cli_tag_test.go ++++++ b/integration-cli/docker_cli_tag_test.go +++@@ -4,7 +4,7 @@ import ( +++ "strings" +++ +++ "github.com/docker/docker/pkg/stringutils" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // tagging a named image in a new unprefixed repo should work +++diff --git a/integration-cli/docker_cli_top_test.go b/integration-cli/docker_cli_top_test.go +++index 667a6c8..cbca969 100644 +++--- a/integration-cli/docker_cli_top_test.go ++++++ b/integration-cli/docker_cli_top_test.go +++@@ -3,7 +3,7 @@ package main +++ import ( +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func (s *DockerSuite) TestTopMultipleArgs(c *check.C) { +++diff --git a/integration-cli/docker_cli_v2_only.go b/integration-cli/docker_cli_v2_only.go +++index aa3d6a2..539f459 100644 +++--- a/integration-cli/docker_cli_v2_only.go ++++++ b/integration-cli/docker_cli_v2_only.go +++@@ -6,7 +6,7 @@ import ( +++ "net/http" +++ "os" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ func makefile(contents string) (string, func(), error) { +++diff --git a/integration-cli/docker_cli_version_test.go b/integration-cli/docker_cli_version_test.go +++index f2d8b65..b1ea96c 100644 +++--- a/integration-cli/docker_cli_version_test.go ++++++ b/integration-cli/docker_cli_version_test.go +++@@ -3,7 +3,7 @@ package main +++ import ( +++ "strings" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // ensure docker version works +++diff --git a/integration-cli/docker_cli_wait_test.go b/integration-cli/docker_cli_wait_test.go +++index 167ea1a..0503fbb 100644 +++--- a/integration-cli/docker_cli_wait_test.go ++++++ b/integration-cli/docker_cli_wait_test.go +++@@ -6,7 +6,7 @@ import ( +++ "strings" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // non-blocking wait with 0 exit code +++diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go +++index e80015d..fcb3242 100644 +++--- a/integration-cli/docker_utils.go ++++++ b/integration-cli/docker_utils.go +++@@ -24,7 +24,7 @@ import ( +++ "github.com/docker/docker/opts" +++ "github.com/docker/docker/pkg/ioutils" +++ "github.com/docker/docker/pkg/stringutils" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ // Daemon represents a Docker daemon for the testing framework. +++diff --git a/integration-cli/registry.go b/integration-cli/registry.go +++index 35e1b4e..4aee021 100644 +++--- a/integration-cli/registry.go ++++++ b/integration-cli/registry.go +++@@ -9,7 +9,7 @@ import ( +++ "path/filepath" +++ +++ "github.com/docker/distribution/digest" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ const v2binary = "registry-v2" +++diff --git a/integration-cli/registry_mock.go b/integration-cli/registry_mock.go +++index e5fb64c..9599eb2 100644 +++--- a/integration-cli/registry_mock.go ++++++ b/integration-cli/registry_mock.go +++@@ -7,7 +7,7 @@ import ( +++ "strings" +++ "sync" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ type handlerFunc func(w http.ResponseWriter, r *http.Request) +++diff --git a/integration-cli/requirements.go b/integration-cli/requirements.go +++index ce080d5..a08febe 100644 +++--- a/integration-cli/requirements.go ++++++ b/integration-cli/requirements.go +++@@ -10,7 +10,7 @@ import ( +++ "strings" +++ "time" +++ +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ type testCondition func() bool +++diff --git a/integration-cli/trust_server.go b/integration-cli/trust_server.go +++index 89d88a8..dcd245d 100644 +++--- a/integration-cli/trust_server.go ++++++ b/integration-cli/trust_server.go +++@@ -12,7 +12,7 @@ import ( +++ "time" +++ +++ "github.com/docker/docker/pkg/tlsconfig" +++- "github.com/go-check/check" ++++ "gopkg.in/check.v1" +++ ) +++ +++ var notaryBinary = "notary-server" diff --cc debian/patches/distribution-2.1.1.patch index 00000000,00000000,00000000..1cc421b2 new file mode 100644 --- /dev/null +++ b/debian/patches/distribution-2.1.1.patch @@@@ -1,0 -1,0 -1,0 +1,17 @@@@ +++Description: Minor changes for github.com/docker/distribution 2.1.1 +++Forwarded: not-needed +++Author: Tianon Gravi +++ +++Index: pkg/registry/registry.go +++=================================================================== +++--- pkg.orig/registry/registry.go ++++++ pkg/registry/registry.go +++@@ -185,7 +185,7 @@ func addRequiredHeadersToRedirectedReque +++ func shouldV2Fallback(err errcode.Error) bool { +++ logrus.Debugf("v2 error: %T %v", err, err) +++ switch err.Code { +++- case v2.ErrorCodeUnauthorized, v2.ErrorCodeManifestUnknown: ++++ case errcode.ErrorCodeUnauthorized, v2.ErrorCodeManifestUnknown: +++ return true +++ } +++ return false diff --cc debian/patches/fatal-error-old-kernels.patch index 00000000,00000000,00000000..83cb2472 new file mode 100644 --- /dev/null +++ b/debian/patches/fatal-error-old-kernels.patch @@@@ -1,0 -1,0 -1,0 +1,18 @@@@ +++Description: Docker is unsupported on kernels < 3.10, so this turns the warning into a fatal error (hard failure) +++Forwarded: not-needed +++Author: Tianon Gravi +++Reviewed-by: Paul Tagliamonte +++ +++diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go +++index 1bc394c..96e3e62 100644 +++--- a/daemon/daemon_unix.go ++++++ b/daemon/daemon_unix.go +++@@ -110,7 +110,7 @@ func checkKernel() error { +++ } else { +++ if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 10, Minor: 0}) < 0 { +++ if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" { +++- logrus.Warnf("You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.10.0.", k.String()) ++++ logrus.Fatalf("ERROR: You are running Linux kernel version %s, which is unsupported for running Docker. Please upgrade your kernel to 3.10+.", k.String()) +++ } +++ } +++ } diff --cc debian/patches/overlay.patch index 00000000,00000000,00000000..df6db677 new file mode 100644 --- /dev/null +++ b/debian/patches/overlay.patch @@@@ -1,0 -1,0 -1,0 +1,18 @@@@ +++Description: move "overlay" to a higher priority (now that AUFS is not supported in Debian kernels) +++Author: Tianon Gravi +++Forwarded: no +++ +++diff --git a/daemon/graphdriver/driver_linux.go b/daemon/graphdriver/driver_linux.go +++index 410a62f..eab78e7 100644 +++--- a/daemon/graphdriver/driver_linux.go ++++++ b/daemon/graphdriver/driver_linux.go +++@@ -48,8 +48,8 @@ var ( +++ "aufs", +++ "btrfs", +++ "zfs", +++- "devicemapper", +++ "overlay", ++++ "devicemapper", +++ "vfs", +++ } +++ diff --cc debian/patches/runc-0.0.4.patch index 00000000,00000000,00000000..9d50b56e new file mode 100644 --- /dev/null +++ b/debian/patches/runc-0.0.4.patch @@@@ -1,0 -1,0 -1,0 +1,68 @@@@ +++Description: Minor changes for github.com/opencontainers/runc 0.0.4 +++Origin: https://github.com/docker/docker/pull/16244 +++Author: David Calavera , Tianon Gravi +++ +++diff --git a/daemon/container.go b/daemon/container.go +++index 49c1f41..727bd69 100644 +++--- a/daemon/container.go ++++++ b/daemon/container.go +++@@ -1088,12 +1088,9 @@ func copyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) +++ +++ func (container *Container) networkMounts() []execdriver.Mount { +++ var mounts []execdriver.Mount +++- mode := "Z" +++- if container.hostConfig.NetworkMode.IsContainer() { +++- mode = "z" +++- } ++++ shared := container.hostConfig.NetworkMode.IsContainer() +++ if container.ResolvConfPath != "" { +++- label.Relabel(container.ResolvConfPath, container.MountLabel, mode) ++++ label.Relabel(container.ResolvConfPath, container.MountLabel, shared) +++ mounts = append(mounts, execdriver.Mount{ +++ Source: container.ResolvConfPath, +++ Destination: "/etc/resolv.conf", +++@@ -1102,7 +1099,7 @@ func (container *Container) networkMounts() []execdriver.Mount { +++ }) +++ } +++ if container.HostnamePath != "" { +++- label.Relabel(container.HostnamePath, container.MountLabel, mode) ++++ label.Relabel(container.HostnamePath, container.MountLabel, shared) +++ mounts = append(mounts, execdriver.Mount{ +++ Source: container.HostnamePath, +++ Destination: "/etc/hostname", +++@@ -1111,7 +1108,7 @@ func (container *Container) networkMounts() []execdriver.Mount { +++ }) +++ } +++ if container.HostsPath != "" { +++- label.Relabel(container.HostsPath, container.MountLabel, mode) ++++ label.Relabel(container.HostsPath, container.MountLabel, shared) +++ mounts = append(mounts, execdriver.Mount{ +++ Source: container.HostsPath, +++ Destination: "/etc/hosts", +++diff --git a/daemon/create.go b/daemon/create.go +++index a4a740f..655bd8f 100644 +++--- a/daemon/create.go ++++++ b/daemon/create.go +++@@ -123,7 +123,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos +++ if err != nil { +++ return nil, nil, err +++ } +++- if err := label.Relabel(v.Path(), container.MountLabel, "z"); err != nil { ++++ if err := label.Relabel(v.Path(), container.MountLabel, true); err != nil { +++ return nil, nil, err +++ } +++ +++diff --git a/daemon/volumes.go b/daemon/volumes.go +++index 556e304..ba2b017 100644 +++--- a/daemon/volumes.go ++++++ b/daemon/volumes.go +++@@ -231,7 +231,8 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc +++ } +++ } +++ +++- if err := label.Relabel(bind.Source, container.MountLabel, bind.Relabel); err != nil { ++++ shared := label.IsShared(bind.Relabel) ++++ if err := label.Relabel(bind.Source, container.MountLabel, shared); err != nil { +++ return err +++ } +++ binds[bind.Destination] = true diff --cc debian/patches/series index 00000000,00000000,00000000..b7b4fdf2 new file mode 100644 --- /dev/null +++ b/debian/patches/series @@@@ -1,0 -1,0 -1,0 +1,19 @@@@ +++cgroupfs-mount-convenience-copy.patch +++ +++# If upstream ever resolves https://github.com/docker/docker/issues/8969 in a +++# reasonable way, remove this patch. +++fatal-error-old-kernels.patch +++ +++# Once upstream adds EnvFile, remove this patch. +++change-system-unit-env-file.patch +++# See also https://github.com/docker/docker/pull/7220#issuecomment-50076589 +++ +++# change "github.com/go-check/check" to "gopkg.in/check.v1" +++check-v1.patch +++ +++15404.patch +++distribution-2.1.1.patch +++runc-0.0.4.patch +++ +++overlay.patch +++cgroupdriver-cgroupfs.patch diff --cc debian/repack.sh index 00000000,00000000,00000000..0429d355 new file mode 100755 --- /dev/null +++ b/debian/repack.sh @@@@ -1,0 -1,0 -1,0 +1,53 @@@@ +++#!/bin/bash +++# Taken from the X Strike Force Build System +++ +++set -e +++ +++debDir="$(pwd)/debian" +++ +++if [ ! -d "$debDir/repack/prune" ]; then +++ exit 0 +++fi +++ +++if [ '--upstream-version' != "$1" ]; then +++ exit 1 +++fi +++ +++version="$2" +++filename="$3" +++ +++if [ -z "$version" ] || [ ! -f "$filename" ]; then +++ exit 1 +++fi +++ +++dir="$(dirname "$filename")" +++filename="$(basename "$filename")" +++dir="$(readlink -f "$dir")" +++tempdir="$(mktemp -d)" +++ +++cd "$tempdir" +++tar xf "$dir/$filename" +++cat "$debDir"/repack/prune/* | while read file; do +++ if [ -e */"$file" ]; then +++ echo "Pruning $file" +++ rm -rf */"$file" +++ fi +++done +++ +++dfsgfilename="$filename" +++if [[ "$dfsgfilename" != *[~+]ds* ]]; then +++ pkg="$(dpkg-parsechangelog -l"$debDir/changelog" -SSource)" +++ ver="$(dpkg-parsechangelog -l"$debDir/changelog" -SVersion)" +++ origVer="${ver%-*}" # strip everything from the last dash +++ origVer="$(echo "$origVer" | sed -r 's/^[0-9]+://')" # strip epoch +++ upstreamVer="${origVer%%[+~]ds*}" +++ dfsgBits="${origVer#$upstreamVer}" +++ +++ dfsgfilename="${dfsgfilename/.orig/$dfsgBits.orig}" +++fi +++tar -czf "$dir/$dfsgfilename" * +++cd "$dir" +++rm -rf "$tempdir" +++echo "Done pruning upstream tarball into $dfsgfilename" +++ +++exit 0 diff --cc debian/repack/prune/vendor index 00000000,00000000,00000000..1acf52e9 new file mode 100644 --- /dev/null +++ b/debian/repack/prune/vendor @@@@ -1,0 -1,0 -1,0 +1,2 @@@@ +++./vendor +++./Godeps/_workspace diff --cc debian/rules index 00000000,00000000,00000000..d2583651 new file mode 100755 --- /dev/null +++ b/debian/rules @@@@ -1,0 -1,0 -1,0 +1,122 @@@@ +++#!/usr/bin/make -f +++# -*- makefile -*- +++ +++DOCKER_GOPKG = github.com/docker/docker +++ +++# MUST STAY IN SYNC WITH LIST IN "debian/helpers/download-libcontainer" +++MULTI_ORIG_GOPKGS = \ +++ github.com/docker/libnetwork +++ +++# the list of Go packages to include in golang-github-docker-docker-dev +++DEV_GOPKGS = \ +++ github.com/docker/docker/autogen/dockerversion \ +++ github.com/docker/docker/pkg +++ +++# temporary build path (see http://golang.org/doc/code.html#GOPATH) +++export GOPATH = $(CURDIR)/.gopath +++GOPATH_PACKAGED = /usr/share/gocode +++ +++# a few helpful variables for deduplication +++DOCKER_BINPATH = /usr/bin/docker +++DOCKER_VERSION = $(shell cat VERSION) +++ +++export DOCKER_GITCOMMIT = $(shell ./debian/helpers/gitcommit.sh $(DOCKER_VERSION)) +++export DOCKER_INITPATH = /usr/lib/docker.io/dockerinit +++ +++# AppArmor can be optionally used in Debian and is there by default in Ubuntu, so we need support for it compiled into our binary +++# same story with SELinux +++export DOCKER_BUILDTAGS = apparmor selinux +++ +++ +++APPARMOR_RECOMMENDS = $(shell dpkg-vendor --is Ubuntu && echo apparmor) +++BUILT_LIBC := $(shell dpkg-query -f '$${source:Package} (= $${source:Version})' -W libc-dev-bin) +++ +++override_dh_gencontrol: +++ echo 'apparmor:Recommends=$(APPARMOR_RECOMMENDS)' >> debian/docker.io.substvars +++ echo 'libc:Built-Using=$(BUILT_LIBC)' >> debian/docker.io.substvars +++ dh_gencontrol +++ +++ +++setup-gopath: +++ # make sure we have our multitarball deps (from tarballs or from vendor/ in nightlies) +++ @set -ex; \ +++ for package in $(MULTI_ORIG_GOPKGS); do \ +++ [ -d $$(basename $$package) ] || { [ -d vendor/src/$$package ] && ln -sf vendor/src/$$package $$(basename $$package); }; \ +++ done +++ +++ # we need to make sure all deps are in our GOPATH +++ mkdir -p "$(GOPATH)" +++ @set -ex; \ +++ for package in $(DOCKER_GOPKG) $(MULTI_ORIG_GOPKGS); do \ +++ [ $$package = $(DOCKER_GOPKG) ] \ +++ && src=. \ +++ || src=$$(basename $$package); \ +++ mkdir -p "$$GOPATH/src/$$(dirname $$package)"; \ +++ ln -sfT "$$(readlink -f "$$src")" "$$GOPATH/src/$$package"; \ +++ done +++ +++override_dh_auto_build: +++ @bash -c '{ [ "$$DOCKER_GITCOMMIT" ]; } || { echo; echo "error: missing DOCKER_GITCOMMIT - see debian/upstream-version-gitcommits"; echo; exit 2; } >&2' +++ +++ ./debian/rules setup-gopath +++ +++ # build "docker" and "dockerinit" +++ GOPATH="$$GOPATH:$(GOPATH_PACKAGED)" \ +++ ./hack/make.sh dynbinary +++ +++ # compile man pages +++ ./man/md2man-all.sh -q +++ +++ +++override_dh_auto_install: +++ # install docker binary +++ mkdir -p debian/docker.io/$(dir $(DOCKER_BINPATH)) +++ cp -aT bundles/$(DOCKER_VERSION)/dynbinary/docker-$(DOCKER_VERSION) debian/docker.io/$(DOCKER_BINPATH) +++ +++ # install dockerinit binary +++ mkdir -p debian/docker.io/$(dir $(DOCKER_INITPATH)) +++ cp -aT bundles/$(DOCKER_VERSION)/dynbinary/dockerinit-$(DOCKER_VERSION) debian/docker.io/$(DOCKER_INITPATH) +++ +++ # Most of the source of docker does not make a library, +++ # so only ship the reusable parts (and in a separate package). +++ @set -ex; \ +++ for package in $(DEV_GOPKGS); do \ +++ mkdir -p "debian/golang-github-docker-docker-dev/$(GOPATH_PACKAGED)/src/$$package"; \ +++ cp -aT "$$(readlink -f "$$GOPATH/src/$$package")" "debian/golang-github-docker-docker-dev/$(GOPATH_PACKAGED)/src/$$package"; \ +++ done +++ +++ +++override_dh_strip: +++ # the SHA1 of dockerinit is important: don't strip it +++ # also, Go has lots of problems with stripping, so just don't +++ +++ +++override_dh_auto_test: +++ +++ +++override_dh_installinit: +++ dh_installinit --name=docker --no-restart-on-upgrade +++ +++ +++override_dh_installudev: +++ # use priority z80 to match the upstream priority of 80 +++ dh_installudev --priority=z80 +++ +++ +++override_dh_auto_clean: +++ dh_auto_clean +++ +++ # GOPATH is created by us +++ rm -rf "$(GOPATH)" +++ +++ # autogen is created by hack/make.sh +++ # bundles is created by hack/make.sh +++ # man/man*/ is created by man/md2man-all.sh +++ rm -rf autogen bundles man/man*/ +++ +++ # TODO FIGURE OUT WHY THIS IS CREATED AND SEND A PR UPSTREAM!!! +++ rm -f a.out +++ +++ +++%: +++ dh $@ --with=systemd,bash-completion diff --cc debian/source.lintian-overrides index 00000000,00000000,00000000..8b17d44d new file mode 100644 --- /dev/null +++ b/debian/source.lintian-overrides @@@@ -1,0 -1,0 -1,0 +1,6 @@@@ +++docker.io source: source-contains-unsafe-symlink utils/testdata/fs/g +++docker.io source: source-contains-unsafe-symlink utils/testdata/fs/a/d +++# Hilariously, these are used to test unsafe symlinks. +++# much testing +++# wow +++# very unsafe diff --cc debian/source/format index 00000000,00000000,00000000..163aaf8d new file mode 100644 --- /dev/null +++ b/debian/source/format @@@@ -1,0 -1,0 -1,0 +1,1 @@@@ +++3.0 (quilt) diff --cc debian/tests/control index 00000000,00000000,00000000..30e76317 new file mode 100644 --- /dev/null +++ b/debian/tests/control @@@@ -1,0 -1,0 -1,0 +1,3 @@@@ +++Tests: integration +++Depends: ca-certificates, curl, git, golang-check.v1-dev, @, @builddeps@ +++Restrictions: allow-stderr isolation-machine needs-root rw-build-tree diff --cc debian/tests/integration index 00000000,00000000,00000000..7c8c8d4a new file mode 100755 --- /dev/null +++ b/debian/tests/integration @@@@ -1,0 -1,0 -1,0 +1,23 @@@@ +++#!/bin/bash +++set -e +++ +++# apply patches +++dpkg-source --before-build . +++ +++# prepare the environment +++./debian/rules setup-gopath +++export GOPATH="$PWD/.gopath:/usr/share/gocode" +++export DOCKER_GITCOMMIT="$(./debian/helpers/gitcommit.sh)" +++ +++# docker's tests need an unprivileged user available at this username +++useradd --system --gid docker --comment 'Docker Test Suite Unprivileged User' unprivilegeduser +++ +++# run the tests +++./hack/make.sh test-integration-cli +++ +++# clean up cruft we've created +++./debian/rules clean +++userdel --force unprivilegeduser +++ +++# unapply patches +++dpkg-source --after-build . diff --cc debian/upstream-version-gitcommits index 00000000,00000000,00000000..423cef91 new file mode 100644 --- /dev/null +++ b/debian/upstream-version-gitcommits @@@@ -1,0 -1,0 -1,0 +1,77 @@@@ +++# To determine the proper value for this, download +++# https://get.docker.io/builds/Linux/x86_64/docker-VERSION, chmod +x, and then +++# run ./docker-VERSION -v, which will list the exact build hash needed. +++ +++0.5.3: 17c92b8 +++0.6.0: f4a4f1c +++0.6.1: 5105263 +++0.6.2: 081543c +++0.6.3: b0a49a3 +++0.6.4: 2f74b1c +++0.6.5: 3ff8459 +++0.6.6: 6d42040 +++0.6.7: cb48ecc +++0.7.0: 0d078b6 +++0.7.1: 88df052 +++0.7.2: 28b162e +++0.7.3: 8502ad4 +++0.7.4: 010d74e +++0.7.5: c348c04 +++0.7.6: bc3b2ec +++0.8.0: cc3a8c8 +++0.8.1: a1598d1 +++0.9.0: 2b3fdf2 +++0.9.1: 3600720 +++0.10.0: dc9c28f +++0.11.0: 15209c3 +++0.11.1: fb99f99 +++0.12.0: 14680bf +++1.0.0: 63fe64c +++1.0.1: 990021a +++1.1.0: 79812e3 +++1.1.1: bd609d2 +++1.1.2: d84a070 +++1.2.0: fa7b24f +++1.3.0: c78088f +++1.3.1: 4e9bbfa +++1.3.2: 39fa2fa +++1.3.3: d344625 +++1.4.0: 4595d4f +++1.4.1: 5bc2ff8 +++1.5.0-rc2: a393450 +++1.5.0-rc3: c02092d +++1.5.0-rc4: a1cae77 +++1.5.0: a8a31ef +++1.6.0-rc1: 746e830 +++1.6.0-rc2: c5ee149 +++1.6.0-rc3: 20d4e6f +++1.6.0-rc4: e2e39fc +++1.6.0-rc6: f181f77 +++1.6.0-rc7: 7e26e41 +++1.6.0: 4749651 +++1.6.1: 97cd073 +++1.6.2: 7c8fca2 +++1.7.0-rc1: 395cced +++1.7.0-rc2: 7ddecf7 +++1.7.0-rc3: 94159c9 +++1.7.0-rc4: cc5c791 +++1.7.0-rc5: f417602 +++1.7.0: 0baf609 +++1.7.1-rc1: f8f912a +++1.7.1-rc2: 3ff5c86 +++1.7.1-rc3: c2e658d +++1.7.1: 786b29d +++1.8.0-rc1: 4f1c66a +++1.8.0-rc2: 8a9d4ea +++1.8.0-rc3: 696147b +++1.8.0: 0d03096 +++1.8.1: d12ea79 +++1.8.2-rc1: 28220ac +++1.8.2: 0a8c2e3 +++1.8.3: f4bf5c7 +++1.9.0-rc1: 9291a0e +++1.9.0-rc2: 60d36f7 +++1.9.0-rc3: 2100b94 +++1.9.0-rc4: e6f5a3c +++1.9.0-rc5: 9318004 +++1.9.0: 76d6bc9 diff --cc debian/vim-syntax-docker.install index 00000000,00000000,00000000..bcc58c5d new file mode 100644 --- /dev/null +++ b/debian/vim-syntax-docker.install @@@@ -1,0 -1,0 -1,0 +1,4 @@@@ +++contrib/syntax/vim/doc/* /usr/share/vim/addons/doc/ +++contrib/syntax/vim/ftdetect/* /usr/share/vim/addons/ftdetect/ +++contrib/syntax/vim/syntax/* /usr/share/vim/addons/syntax/ +++debian/vim-syntax-docker.yaml /usr/share/vim/registry/ diff --cc debian/vim-syntax-docker.yaml index 00000000,00000000,00000000..517b5914 new file mode 100644 --- /dev/null +++ b/debian/vim-syntax-docker.yaml @@@@ -1,0 -1,0 -1,0 +1,6 @@@@ +++addon: dockerfile +++description: "Addon to highlight Docker's Dockerfiles" +++files: +++ - doc/dockerfile.txt +++ - ftdetect/dockerfile.vim +++ - syntax/dockerfile.vim diff --cc debian/watch index 00000000,00000000,00000000..05c1f00e new file mode 100644 --- /dev/null +++ b/debian/watch @@@@ -1,0 -1,0 -1,0 +1,6 @@@@ +++version=3 +++opts=\ +++dversionmangle=s/[+~](debian|dfsg|ds|deb)\d*$//,\ +++uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/,\ +++filenamemangle=s/.+\/v(\d\S*)\.tar\.gz/docker.io_$1.orig.tar.gz/ \ +++ https://github.com/docker/docker/tags .*/v(\d\S*)\.tar\.gz debian ./debian/repack.sh diff --cc libnetwork/.gitignore index 00000000,00000000,00000000..c03c9653 new file mode 100644 --- /dev/null +++ b/libnetwork/.gitignore @@@@ -1,0 -1,0 -1,0 +1,33 @@@@ +++# Compiled Object files, Static and Dynamic libs (Shared Objects) +++*.o +++*.a +++*.so +++ +++# Folders +++_obj +++_test +++ +++# Architecture specific extensions/prefixes +++*.[568vq] +++[568vq].out +++ +++*.cgo1.go +++*.cgo2.c +++_cgo_defun.c +++_cgo_gotypes.go +++_cgo_export.* +++ +++_testmain.go +++ +++*.exe +++*.test +++*.prof +++ +++# Coverage +++*.tmp +++*.coverprofile +++ +++# IDE files +++.project +++ +++libnetwork-build.created diff --cc libnetwork/Godeps/Godeps.json index 00000000,00000000,00000000..23b92956 new file mode 100644 --- /dev/null +++ b/libnetwork/Godeps/Godeps.json @@@@ -1,0 -1,0 -1,0 +1,137 @@@@ +++{ +++ "ImportPath": "github.com/docker/libnetwork", +++ "GoVersion": "go1.4.2", +++ "Packages": [ +++ "./..." +++ ], +++ "Deps": [ +++ { +++ "ImportPath": "github.com/BurntSushi/toml", +++ "Comment": "v0.1.0-16-gf706d00", +++ "Rev": "f706d00e3de6abe700c994cdd545a1a4915af060" +++ }, +++ { +++ "ImportPath": "github.com/Sirupsen/logrus", +++ "Comment": "v0.6.4-12-g467d9d5", +++ "Rev": "467d9d55c2d2c17248441a8fc661561161f40d5e" +++ }, +++ { +++ "ImportPath": "github.com/armon/go-metrics", +++ "Rev": "eb0af217e5e9747e41dd5303755356b62d28e3ec" +++ }, +++ { +++ "ImportPath": "github.com/coreos/go-etcd/etcd", +++ "Comment": "v2.0.0-7-g73a8ef7", +++ "Rev": "73a8ef737e8ea002281a28b4cb92a1de121ad4c6" +++ }, +++ { +++ "ImportPath": "github.com/docker/docker/pkg/homedir", +++ "Comment": "v1.4.1-4106-g637023a", +++ "Rev": "637023a5f8d8347a0e271c09d5c9bc84fbc97693" +++ }, +++ { +++ "ImportPath": "github.com/docker/docker/pkg/ioutils", +++ "Comment": "v1.4.1-4106-g637023a", +++ "Rev": "637023a5f8d8347a0e271c09d5c9bc84fbc97693" +++ }, +++ { +++ "ImportPath": "github.com/docker/docker/pkg/mflag", +++ "Comment": "v1.4.1-4106-g637023a", +++ "Rev": "637023a5f8d8347a0e271c09d5c9bc84fbc97693" +++ }, +++ { +++ "ImportPath": "github.com/docker/docker/pkg/parsers", +++ "Comment": "v1.4.1-4106-g637023a", +++ "Rev": "637023a5f8d8347a0e271c09d5c9bc84fbc97693" +++ }, +++ { +++ "ImportPath": "github.com/docker/docker/pkg/plugins", +++ "Comment": "v1.4.1-4106-g637023a", +++ "Rev": "637023a5f8d8347a0e271c09d5c9bc84fbc97693" +++ }, +++ { +++ "ImportPath": "github.com/docker/docker/pkg/proxy", +++ "Comment": "v1.4.1-4106-g637023a", +++ "Rev": "637023a5f8d8347a0e271c09d5c9bc84fbc97693" +++ }, +++ { +++ "ImportPath": "github.com/docker/docker/pkg/reexec", +++ "Comment": "v1.4.1-4106-g637023a", +++ "Rev": "637023a5f8d8347a0e271c09d5c9bc84fbc97693" +++ }, +++ { +++ "ImportPath": "github.com/docker/docker/pkg/stringid", +++ "Comment": "v1.4.1-4106-g637023a", +++ "Rev": "637023a5f8d8347a0e271c09d5c9bc84fbc97693" +++ }, +++ { +++ "ImportPath": "github.com/docker/docker/pkg/term", +++ "Comment": "v1.4.1-4106-g637023a", +++ "Rev": "637023a5f8d8347a0e271c09d5c9bc84fbc97693" +++ }, +++ { +++ "ImportPath": "github.com/docker/libcontainer/user", +++ "Comment": "v1.4.0-495-g3e66118", +++ "Rev": "3e661186ba24f259d3860f067df052c7f6904bee" +++ }, +++ { +++ "ImportPath": "github.com/docker/libkv", +++ "Rev": "60c7c881345b3c67defc7f93a8297debf041d43c" +++ }, +++ { +++ "ImportPath": "github.com/godbus/dbus", +++ "Comment": "v2-3-g4160802", +++ "Rev": "41608027bdce7bfa8959d653a00b954591220e67" +++ }, +++ { +++ "ImportPath": "github.com/gorilla/context", +++ "Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd" +++ }, +++ { +++ "ImportPath": "github.com/gorilla/mux", +++ "Rev": "8096f47503459bcc74d1f4c487b7e6e42e5746b5" +++ }, +++ { +++ "ImportPath": "github.com/hashicorp/consul/api", +++ "Comment": "v0.5.0rc1-66-g954aec6", +++ "Rev": "954aec66231b79c161a4122b023fbcad13047f79" +++ }, +++ { +++ "ImportPath": "github.com/hashicorp/go-msgpack/codec", +++ "Rev": "71c2886f5a673a35f909803f38ece5810165097b" +++ }, +++ { +++ "ImportPath": "github.com/hashicorp/memberlist", +++ "Rev": "9a1e242e454d2443df330bdd51a436d5a9058fc4" +++ }, +++ { +++ "ImportPath": "github.com/hashicorp/serf/serf", +++ "Comment": "v0.6.4", +++ "Rev": "7151adcef72687bf95f451a2e0ba15cb19412bf2" +++ }, +++ { +++ "ImportPath": "github.com/samuel/go-zookeeper/zk", +++ "Rev": "d0e0d8e11f318e000a8cc434616d69e329edc374" +++ }, +++ { +++ "ImportPath": "github.com/stretchr/objx", +++ "Rev": "cbeaeb16a013161a98496fad62933b1d21786672" +++ }, +++ { +++ "ImportPath": "github.com/stretchr/testify/assert", +++ "Rev": "dab07ac62d4905d3e48d17dc549c684ac3b7c15a" +++ }, +++ { +++ "ImportPath": "github.com/stretchr/testify/mock", +++ "Rev": "dab07ac62d4905d3e48d17dc549c684ac3b7c15a" +++ }, +++ { +++ "ImportPath": "github.com/vishvananda/netns", +++ "Rev": "493029407eeb434d0c2d44e02ea072ff2488d322" +++ }, +++ { +++ "ImportPath": "github.com/vishvananda/netlink", +++ "Rev": "4b5dce31de6d42af5bb9811c6d265472199e0fec" +++ } +++ ] +++} diff --cc libnetwork/Godeps/Readme index 00000000,00000000,00000000..4cdaa53d new file mode 100644 --- /dev/null +++ b/libnetwork/Godeps/Readme @@@@ -1,0 -1,0 -1,0 +1,5 @@@@ +++This directory tree is generated automatically by godep. +++ +++Please do not edit. +++ +++See https://github.com/tools/godep for more information. diff --cc libnetwork/LICENSE index 00000000,00000000,00000000..e06d2081 new file mode 100644 --- /dev/null +++ b/libnetwork/LICENSE @@@@ -1,0 -1,0 -1,0 +1,202 @@@@ +++Apache License +++ Version 2.0, January 2004 +++ http://www.apache.org/licenses/ +++ +++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +++ +++ 1. Definitions. +++ +++ "License" shall mean the terms and conditions for use, reproduction, +++ and distribution as defined by Sections 1 through 9 of this document. +++ +++ "Licensor" shall mean the copyright owner or entity authorized by +++ the copyright owner that is granting the License. +++ +++ "Legal Entity" shall mean the union of the acting entity and all +++ other entities that control, are controlled by, or are under common +++ control with that entity. For the purposes of this definition, +++ "control" means (i) the power, direct or indirect, to cause the +++ direction or management of such entity, whether by contract or +++ otherwise, or (ii) ownership of fifty percent (50%) or more of the +++ outstanding shares, or (iii) beneficial ownership of such entity. +++ +++ "You" (or "Your") shall mean an individual or Legal Entity +++ exercising permissions granted by this License. +++ +++ "Source" form shall mean the preferred form for making modifications, +++ including but not limited to software source code, documentation +++ source, and configuration files. +++ +++ "Object" form shall mean any form resulting from mechanical +++ transformation or translation of a Source form, including but +++ not limited to compiled object code, generated documentation, +++ and conversions to other media types. +++ +++ "Work" shall mean the work of authorship, whether in Source or +++ Object form, made available under the License, as indicated by a +++ copyright notice that is included in or attached to the work +++ (an example is provided in the Appendix below). +++ +++ "Derivative Works" shall mean any work, whether in Source or Object +++ form, that is based on (or derived from) the Work and for which the +++ editorial revisions, annotations, elaborations, or other modifications +++ represent, as a whole, an original work of authorship. For the purposes +++ of this License, Derivative Works shall not include works that remain +++ separable from, or merely link (or bind by name) to the interfaces of, +++ the Work and Derivative Works thereof. +++ +++ "Contribution" shall mean any work of authorship, including +++ the original version of the Work and any modifications or additions +++ to that Work or Derivative Works thereof, that is intentionally +++ submitted to Licensor for inclusion in the Work by the copyright owner +++ or by an individual or Legal Entity authorized to submit on behalf of +++ the copyright owner. For the purposes of this definition, "submitted" +++ means any form of electronic, verbal, or written communication sent +++ to the Licensor or its representatives, including but not limited to +++ communication on electronic mailing lists, source code control systems, +++ and issue tracking systems that are managed by, or on behalf of, the +++ Licensor for the purpose of discussing and improving the Work, but +++ excluding communication that is conspicuously marked or otherwise +++ designated in writing by the copyright owner as "Not a Contribution." +++ +++ "Contributor" shall mean Licensor and any individual or Legal Entity +++ on behalf of whom a Contribution has been received by Licensor and +++ subsequently incorporated within the Work. +++ +++ 2. Grant of Copyright License. Subject to the terms and conditions of +++ this License, each Contributor hereby grants to You a perpetual, +++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable +++ copyright license to reproduce, prepare Derivative Works of, +++ publicly display, publicly perform, sublicense, and distribute the +++ Work and such Derivative Works in Source or Object form. +++ +++ 3. Grant of Patent License. Subject to the terms and conditions of +++ this License, each Contributor hereby grants to You a perpetual, +++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable +++ (except as stated in this section) patent license to make, have made, +++ use, offer to sell, sell, import, and otherwise transfer the Work, +++ where such license applies only to those patent claims licensable +++ by such Contributor that are necessarily infringed by their +++ Contribution(s) alone or by combination of their Contribution(s) +++ with the Work to which such Contribution(s) was submitted. If You +++ institute patent litigation against any entity (including a +++ cross-claim or counterclaim in a lawsuit) alleging that the Work +++ or a Contribution incorporated within the Work constitutes direct +++ or contributory patent infringement, then any patent licenses +++ granted to You under this License for that Work shall terminate +++ as of the date such litigation is filed. +++ +++ 4. Redistribution. You may reproduce and distribute copies of the +++ Work or Derivative Works thereof in any medium, with or without +++ modifications, and in Source or Object form, provided that You +++ meet the following conditions: +++ +++ (a) You must give any other recipients of the Work or +++ Derivative Works a copy of this License; and +++ +++ (b) You must cause any modified files to carry prominent notices +++ stating that You changed the files; and +++ +++ (c) You must retain, in the Source form of any Derivative Works +++ that You distribute, all copyright, patent, trademark, and +++ attribution notices from the Source form of the Work, +++ excluding those notices that do not pertain to any part of +++ the Derivative Works; and +++ +++ (d) If the Work includes a "NOTICE" text file as part of its +++ distribution, then any Derivative Works that You distribute must +++ include a readable copy of the attribution notices contained +++ within such NOTICE file, excluding those notices that do not +++ pertain to any part of the Derivative Works, in at least one +++ of the following places: within a NOTICE text file distributed +++ as part of the Derivative Works; within the Source form or +++ documentation, if provided along with the Derivative Works; or, +++ within a display generated by the Derivative Works, if and +++ wherever such third-party notices normally appear. The contents +++ of the NOTICE file are for informational purposes only and +++ do not modify the License. You may add Your own attribution +++ notices within Derivative Works that You distribute, alongside +++ or as an addendum to the NOTICE text from the Work, provided +++ that such additional attribution notices cannot be construed +++ as modifying the License. +++ +++ You may add Your own copyright statement to Your modifications and +++ may provide additional or different license terms and conditions +++ for use, reproduction, or distribution of Your modifications, or +++ for any such Derivative Works as a whole, provided Your use, +++ reproduction, and distribution of the Work otherwise complies with +++ the conditions stated in this License. +++ +++ 5. Submission of Contributions. Unless You explicitly state otherwise, +++ any Contribution intentionally submitted for inclusion in the Work +++ by You to the Licensor shall be under the terms and conditions of +++ this License, without any additional terms or conditions. +++ Notwithstanding the above, nothing herein shall supersede or modify +++ the terms of any separate license agreement you may have executed +++ with Licensor regarding such Contributions. +++ +++ 6. Trademarks. This License does not grant permission to use the trade +++ names, trademarks, service marks, or product names of the Licensor, +++ except as required for reasonable and customary use in describing the +++ origin of the Work and reproducing the content of the NOTICE file. +++ +++ 7. Disclaimer of Warranty. Unless required by applicable law or +++ agreed to in writing, Licensor provides the Work (and each +++ Contributor provides its Contributions) on an "AS IS" BASIS, +++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +++ implied, including, without limitation, any warranties or conditions +++ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +++ PARTICULAR PURPOSE. You are solely responsible for determining the +++ appropriateness of using or redistributing the Work and assume any +++ risks associated with Your exercise of permissions under this License. +++ +++ 8. Limitation of Liability. In no event and under no legal theory, +++ whether in tort (including negligence), contract, or otherwise, +++ unless required by applicable law (such as deliberate and grossly +++ negligent acts) or agreed to in writing, shall any Contributor be +++ liable to You for damages, including any direct, indirect, special, +++ incidental, or consequential damages of any character arising as a +++ result of this License or out of the use or inability to use the +++ Work (including but not limited to damages for loss of goodwill, +++ work stoppage, computer failure or malfunction, or any and all +++ other commercial damages or losses), even if such Contributor +++ has been advised of the possibility of such damages. +++ +++ 9. Accepting Warranty or Additional Liability. While redistributing +++ the Work or Derivative Works thereof, You may choose to offer, +++ and charge a fee for, acceptance of support, warranty, indemnity, +++ or other liability obligations and/or rights consistent with this +++ License. However, in accepting such obligations, You may act only +++ on Your own behalf and on Your sole responsibility, not on behalf +++ of any other Contributor, and only if You agree to indemnify, +++ defend, and hold each Contributor harmless for any liability +++ incurred by, or claims asserted against, such Contributor by reason +++ of your accepting any such warranty or additional liability. +++ +++ END OF TERMS AND CONDITIONS +++ +++ APPENDIX: How to apply the Apache License to your work. +++ +++ To apply the Apache License to your work, attach the following +++ boilerplate notice, with the fields enclosed by brackets "{}" +++ replaced with your own identifying information. (Don't include +++ the brackets!) The text should be enclosed in the appropriate +++ comment syntax for the file format. We also recommend that a +++ file or class name and description of purpose be included on the +++ same "printed page" as the copyright notice for easier +++ identification within third-party archives. +++ +++ Copyright {yyyy} {name of copyright owner} +++ +++ Licensed under the Apache License, Version 2.0 (the "License"); +++ you may not use this file except in compliance with the License. +++ You may obtain a copy of the License at +++ +++ http://www.apache.org/licenses/LICENSE-2.0 +++ +++ Unless required by applicable law or agreed to in writing, software +++ distributed under the License is distributed on an "AS IS" BASIS, +++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +++ See the License for the specific language governing permissions and +++ limitations under the License. +++ diff --cc libnetwork/MAINTAINERS index 00000000,00000000,00000000..69f1e9b8 new file mode 100644 --- /dev/null +++ b/libnetwork/MAINTAINERS @@@@ -1,0 -1,0 -1,0 +1,5 @@@@ +++Alessandro Boch (@aboch) +++Alexandr Morozov (@LK4D4) +++Arnaud Porterie (@icecrime) +++Jana Radhakrishnan (@mrjana) +++Madhu Venugopal (@mavenugo) diff --cc libnetwork/Makefile index 00000000,00000000,00000000..deb510cf new file mode 100644 --- /dev/null +++ b/libnetwork/Makefile @@@@ -1,0 -1,0 -1,0 +1,79 @@@@ +++.PHONY: all all-local build build-local check check-code check-format run-tests check-local install-deps coveralls circle-ci +++SHELL=/bin/bash +++build_image=libnetwork-build +++dockerargs = --privileged -v $(shell pwd):/go/src/github.com/docker/libnetwork -w /go/src/github.com/docker/libnetwork +++container_env = -e "INSIDECONTAINER=-incontainer=true" +++docker = docker run --rm ${dockerargs} ${container_env} ${build_image} +++ciargs = -e "COVERALLS_TOKEN=$$COVERALLS_TOKEN" -e "INSIDECONTAINER=-incontainer=true" +++cidocker = docker run ${ciargs} ${dockerargs} golang:1.4 +++ +++all: ${build_image}.created +++ ${docker} make all-local +++ +++all-local: check-local build-local +++ +++${build_image}.created: +++ docker run --name=libnetworkbuild -v $(shell pwd):/go/src/github.com/docker/libnetwork -w /go/src/github.com/docker/libnetwork golang:1.4 make install-deps +++ docker commit libnetworkbuild ${build_image} +++ docker rm libnetworkbuild +++ touch ${build_image}.created +++ +++build: ${build_image}.created +++ ${docker} make build-local +++ +++build-local: +++ $(shell which godep) go build -tags libnetwork_discovery ./... +++ +++check: ${build_image}.created +++ ${docker} make check-local +++ +++check-code: +++ @echo "Checking code... " +++ test -z "$$(golint ./... | tee /dev/stderr)" +++ go vet ./... +++ @echo "Done checking code" +++ +++check-format: +++ @echo "Checking format... " +++ test -z "$$(goimports -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)" +++ @echo "Done checking format" +++ +++run-tests: +++ @echo "Running tests... " +++ @echo "mode: count" > coverage.coverprofile +++ @for dir in $$(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d); do \ +++ if ls $$dir/*.go &> /dev/null; then \ +++ pushd . &> /dev/null ; \ +++ cd $$dir ; \ +++ $(shell which godep) go test ${INSIDECONTAINER} -test.parallel 3 -test.v -covermode=count -coverprofile=./profile.tmp ; \ +++ ret=$$? ;\ +++ if [ $$ret -ne 0 ]; then exit $$ret; fi ;\ +++ popd &> /dev/null; \ +++ if [ -f $$dir/profile.tmp ]; then \ +++ cat $$dir/profile.tmp | tail -n +2 >> coverage.coverprofile ; \ +++ rm $$dir/profile.tmp ; \ +++ fi ; \ +++ fi ; \ +++ done +++ @echo "Done running tests" +++ +++check-local: check-format check-code run-tests +++ +++install-deps: +++ apt-get update && apt-get -y install iptables +++ git clone https://github.com/golang/tools /go/src/golang.org/x/tools +++ go install golang.org/x/tools/cmd/vet +++ go install golang.org/x/tools/cmd/goimports +++ go install golang.org/x/tools/cmd/cover +++ go get github.com/tools/godep +++ go get github.com/golang/lint/golint +++ go get github.com/mattn/goveralls +++ +++coveralls: +++ -@goveralls -service circleci -coverprofile=coverage.coverprofile -repotoken $$COVERALLS_TOKEN +++ +++# CircleCI's Docker fails when cleaning up using the --rm flag +++# The following target is a workaround for this +++ +++circle-ci: +++ @${cidocker} make install-deps check-local coveralls diff --cc libnetwork/README.md index 00000000,00000000,00000000..90fcbe01 new file mode 100644 --- /dev/null +++ b/libnetwork/README.md @@@@ -1,0 -1,0 -1,0 +1,88 @@@@ +++# libnetwork - networking for containers +++ +++[![Circle CI](https://circleci.com/gh/docker/libnetwork/tree/master.svg?style=svg)](https://circleci.com/gh/docker/libnetwork/tree/master) [![Coverage Status](https://coveralls.io/repos/docker/libnetwork/badge.svg)](https://coveralls.io/r/docker/libnetwork) [![GoDoc](https://godoc.org/github.com/docker/libnetwork?status.svg)](https://godoc.org/github.com/docker/libnetwork) +++ +++Libnetwork provides a native Go implementation for connecting containers +++ +++The goal of libnetwork is to deliver a robust Container Network Model that provides a consistent programming interface and the required network abstractions for applications. +++ +++**NOTE**: libnetwork project is under heavy development and is not ready for general use. +++ +++#### Design +++Please refer to the [design](docs/design.md) for more information. +++ +++#### Using libnetwork +++ +++There are many networking solutions available to suit a broad range of use-cases. libnetwork uses a driver / plugin model to support all of these solutions while abstracting the complexity of the driver implementations by exposing a simple and consistent Network Model to users. +++ +++ +++```go +++ // Create a new controller instance +++ controller, err := libnetwork.New() +++ if err != nil { +++ return +++ } +++ +++ // Select and configure the network driver +++ networkType := "bridge" +++ +++ driverOptions := options.Generic{} +++ genericOption := make(map[string]interface{}) +++ genericOption[netlabel.GenericData] = driverOptions +++ err := controller.ConfigureNetworkDriver(networkType, genericOption) +++ if err != nil { +++ return +++ } +++ +++ // Create a network for containers to join. +++ // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can make of +++ network, err := controller.NewNetwork(networkType, "network1") +++ if err != nil { +++ return +++ } +++ +++ // For each new container: allocate IP and interfaces. The returned network +++ // settings will be used for container infos (inspect and such), as well as +++ // iptables rules for port publishing. This info is contained or accessible +++ // from the returned endpoint. +++ ep, err := network.CreateEndpoint("Endpoint1") +++ if err != nil { +++ return +++ } +++ +++ // A container can join the endpoint by providing the container ID to the join +++ // api. +++ // Join accepts Variadic arguments which will be made use of by libnetwork and Drivers +++ err = ep.Join("container1", +++ libnetwork.JoinOptionHostname("test"), +++ libnetwork.JoinOptionDomainname("docker.io")) +++ if err != nil { +++ return +++ } +++ +++ // libnetwork client can check the endpoint's operational data via the Info() API +++ epInfo, err := ep.DriverInfo() +++ mapData, ok := epInfo[netlabel.PortMap] +++ if ok { +++ portMapping, ok := mapData.([]netutils.PortBinding) +++ if ok { +++ fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping) +++ } +++ } +++ +++``` +++#### Current Status +++Please watch this space for updates on the progress. +++ +++Currently libnetwork is nothing more than an attempt to modularize the Docker platform's networking subsystem by moving it into libnetwork as a library. +++ +++## Future +++Please refer to [roadmap](ROADMAP.md) for more information. +++ +++## Contributing +++ +++Want to hack on libnetwork? [Docker's contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) apply. +++ +++## Copyright and license +++Code and documentation copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons. +++ diff --cc libnetwork/ROADMAP.md index 00000000,00000000,00000000..9cb3174e new file mode 100644 --- /dev/null +++ b/libnetwork/ROADMAP.md @@@@ -1,0 -1,0 -1,0 +1,20 @@@@ +++# Roadmap +++ +++This document defines the high-level goals of the libnetwork project. See [Project Planning](#project-planning) for information on Releases. +++ +++## Long-term Goal +++ +++libnetwork project will follow Docker and Linux philosophy of delivering small, highly modular and composable tools that works well independently. +++libnetwork aims to satisfy that composable need for Networking in Containers. +++ +++## Short-term Goals +++ +++- Modularize the networking logic in Docker Engine and libcontainer in to a single, reusable library +++- Replace the networking subsystem of Docker Engine, with libnetwork +++- Define a flexible model that allows local and remote drivers to provide networking to containers +++- Provide a stand-alone tool "dnet" for managing and testing libnetwork +++ +++Project Planning +++================ +++ +++[Project Pages](https://github.com/docker/libnetwork/wiki) define the goals for each Milestone and identify the release-relationship to the Docker Platform. diff --cc libnetwork/api/api.go index 00000000,00000000,00000000..2b5b5773 new file mode 100644 --- /dev/null +++ b/libnetwork/api/api.go @@@@ -1,0 -1,0 -1,0 +1,807 @@@@ +++package api +++ +++import ( +++ "encoding/json" +++ "fmt" +++ "io/ioutil" +++ "net/http" +++ "strings" +++ +++ "github.com/docker/libnetwork" +++ "github.com/docker/libnetwork/netlabel" +++ "github.com/docker/libnetwork/types" +++ "github.com/gorilla/mux" +++) +++ +++var ( +++ successResponse = responseStatus{Status: "Success", StatusCode: http.StatusOK} +++ createdResponse = responseStatus{Status: "Created", StatusCode: http.StatusCreated} +++ mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest} +++ badQueryResponse = responseStatus{Status: "Unsupported query", StatusCode: http.StatusBadRequest} +++) +++ +++const ( +++ // Resource name regex +++ regex = "[a-zA-Z_0-9-]+" +++ // Router URL variable definition +++ nwName = "{" + urlNwName + ":" + regex + "}" +++ nwID = "{" + urlNwID + ":" + regex + "}" +++ nwPID = "{" + urlNwPID + ":" + regex + "}" +++ epName = "{" + urlEpName + ":" + regex + "}" +++ epID = "{" + urlEpID + ":" + regex + "}" +++ epPID = "{" + urlEpPID + ":" + regex + "}" +++ cnID = "{" + urlCnID + ":" + regex + "}" +++ +++ // Though this name can be anything, in order to support default network, +++ // we will keep it as name +++ urlNwName = "name" +++ // Internal URL variable name, they can be anything +++ urlNwID = "network-id" +++ urlNwPID = "network-partial-id" +++ urlEpName = "endpoint-name" +++ urlEpID = "endpoint-id" +++ urlEpPID = "endpoint-partial-id" +++ urlCnID = "container-id" +++ +++ // BridgeNetworkDriver is the built-in default for Network Driver +++ BridgeNetworkDriver = "bridge" +++) +++ +++// NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork +++func NewHTTPHandler(c libnetwork.NetworkController) func(w http.ResponseWriter, req *http.Request) { +++ h := &httpHandler{c: c} +++ h.initRouter() +++ return h.handleRequest +++} +++ +++type responseStatus struct { +++ Status string +++ StatusCode int +++} +++ +++func (r *responseStatus) isOK() bool { +++ return r.StatusCode == http.StatusOK || r.StatusCode == http.StatusCreated +++} +++ +++type processor func(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) +++ +++type httpHandler struct { +++ c libnetwork.NetworkController +++ r *mux.Router +++} +++ +++func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) { +++ // Make sure the service is there +++ if h.c == nil { +++ http.Error(w, "NetworkController is not available", http.StatusServiceUnavailable) +++ return +++ } +++ +++ // Get handler from router and execute it +++ h.r.ServeHTTP(w, req) +++} +++ +++func (h *httpHandler) initRouter() { +++ m := map[string][]struct { +++ url string +++ qrs []string +++ fct processor +++ }{ +++ "GET": { +++ // Order matters +++ {"/networks", []string{"name", nwName}, procGetNetworks}, +++ {"/networks", []string{"partial-id", nwPID}, procGetNetworks}, +++ {"/networks", nil, procGetNetworks}, +++ {"/networks/" + nwID, nil, procGetNetwork}, +++ {"/networks/" + nwID + "/endpoints", []string{"name", epName}, procGetEndpoints}, +++ {"/networks/" + nwID + "/endpoints", []string{"partial-id", epPID}, procGetEndpoints}, +++ {"/networks/" + nwID + "/endpoints", nil, procGetEndpoints}, +++ {"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint}, +++ {"/services", []string{"network", nwName}, procGetServices}, +++ {"/services", []string{"name", epName}, procGetServices}, +++ {"/services", []string{"partial-id", epPID}, procGetServices}, +++ {"/services", nil, procGetServices}, +++ {"/services/" + epID, nil, procGetService}, +++ {"/services/" + epID + "/backend", nil, procGetContainers}, +++ }, +++ "POST": { +++ {"/networks", nil, procCreateNetwork}, +++ {"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint}, +++ {"/networks/" + nwID + "/endpoints/" + epID + "/containers", nil, procJoinEndpoint}, +++ {"/services", nil, procPublishService}, +++ {"/services/" + epID + "/backend", nil, procAttachBackend}, +++ }, +++ "DELETE": { +++ {"/networks/" + nwID, nil, procDeleteNetwork}, +++ {"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint}, +++ {"/networks/" + nwID + "/endpoints/" + epID + "/containers/" + cnID, nil, procLeaveEndpoint}, +++ {"/services/" + epID, nil, procUnpublishService}, +++ {"/services/" + epID + "/backend/" + cnID, nil, procDetachBackend}, +++ }, +++ } +++ +++ h.r = mux.NewRouter() +++ for method, routes := range m { +++ for _, route := range routes { +++ r := h.r.Path("/{.*}" + route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct)) +++ if route.qrs != nil { +++ r.Queries(route.qrs...) +++ } +++ +++ r = h.r.Path(route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct)) +++ if route.qrs != nil { +++ r.Queries(route.qrs...) +++ } +++ } +++ } +++} +++ +++func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerFunc { +++ return func(w http.ResponseWriter, req *http.Request) { +++ var ( +++ body []byte +++ err error +++ ) +++ if req.Body != nil { +++ body, err = ioutil.ReadAll(req.Body) +++ if err != nil { +++ http.Error(w, "Invalid body: "+err.Error(), http.StatusBadRequest) +++ return +++ } +++ } +++ +++ mvars := mux.Vars(req) +++ rvars := req.URL.Query() +++ // workaround a mux issue which filters out valid queries with empty value +++ for k := range rvars { +++ if _, ok := mvars[k]; !ok { +++ if rvars.Get(k) == "" { +++ mvars[k] = "" +++ } +++ } +++ } +++ +++ res, rsp := fct(ctrl, mvars, body) +++ if !rsp.isOK() { +++ http.Error(w, rsp.Status, rsp.StatusCode) +++ return +++ } +++ if res != nil { +++ writeJSON(w, rsp.StatusCode, res) +++ } +++ } +++} +++ +++/***************** +++ Resource Builders +++******************/ +++ +++func buildNetworkResource(nw libnetwork.Network) *networkResource { +++ r := &networkResource{} +++ if nw != nil { +++ r.Name = nw.Name() +++ r.ID = nw.ID() +++ r.Type = nw.Type() +++ epl := nw.Endpoints() +++ r.Endpoints = make([]*endpointResource, 0, len(epl)) +++ for _, e := range epl { +++ epr := buildEndpointResource(e) +++ r.Endpoints = append(r.Endpoints, epr) +++ } +++ } +++ return r +++} +++ +++func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource { +++ r := &endpointResource{} +++ if ep != nil { +++ r.Name = ep.Name() +++ r.ID = ep.ID() +++ r.Network = ep.Network() +++ } +++ return r +++} +++ +++func buildContainerResource(ci libnetwork.ContainerInfo) *containerResource { +++ r := &containerResource{} +++ if ci != nil { +++ r.ID = ci.ID() +++ } +++ return r +++} +++ +++/**************** +++ Options Parsers +++*****************/ +++ +++func (nc *networkCreate) parseOptions() []libnetwork.NetworkOption { +++ var setFctList []libnetwork.NetworkOption +++ +++ if nc.Options != nil { +++ setFctList = append(setFctList, libnetwork.NetworkOptionGeneric(nc.Options)) +++ } +++ +++ return setFctList +++} +++ +++func (ej *endpointJoin) parseOptions() []libnetwork.EndpointOption { +++ var setFctList []libnetwork.EndpointOption +++ if ej.HostName != "" { +++ setFctList = append(setFctList, libnetwork.JoinOptionHostname(ej.HostName)) +++ } +++ if ej.DomainName != "" { +++ setFctList = append(setFctList, libnetwork.JoinOptionDomainname(ej.DomainName)) +++ } +++ if ej.HostsPath != "" { +++ setFctList = append(setFctList, libnetwork.JoinOptionHostsPath(ej.HostsPath)) +++ } +++ if ej.ResolvConfPath != "" { +++ setFctList = append(setFctList, libnetwork.JoinOptionResolvConfPath(ej.ResolvConfPath)) +++ } +++ if ej.UseDefaultSandbox { +++ setFctList = append(setFctList, libnetwork.JoinOptionUseDefaultSandbox()) +++ } +++ if ej.DNS != nil { +++ for _, d := range ej.DNS { +++ setFctList = append(setFctList, libnetwork.JoinOptionDNS(d)) +++ } +++ } +++ if ej.ExtraHosts != nil { +++ for _, e := range ej.ExtraHosts { +++ setFctList = append(setFctList, libnetwork.JoinOptionExtraHost(e.Name, e.Address)) +++ } +++ } +++ if ej.ParentUpdates != nil { +++ for _, p := range ej.ParentUpdates { +++ setFctList = append(setFctList, libnetwork.JoinOptionParentUpdate(p.EndpointID, p.Name, p.Address)) +++ } +++ } +++ return setFctList +++} +++ +++/****************** +++ Process functions +++*******************/ +++ +++func processCreateDefaults(c libnetwork.NetworkController, nc *networkCreate) { +++ if nc.NetworkType == "" { +++ nc.NetworkType = c.Config().Daemon.DefaultDriver +++ } +++ if nc.NetworkType == BridgeNetworkDriver { +++ if nc.Options == nil { +++ nc.Options = make(map[string]interface{}) +++ } +++ genericData, ok := nc.Options[netlabel.GenericData] +++ if !ok { +++ genericData = make(map[string]interface{}) +++ } +++ gData := genericData.(map[string]interface{}) +++ +++ if _, ok := gData["BridgeName"]; !ok { +++ gData["BridgeName"] = nc.Name +++ } +++ if _, ok := gData["AllowNonDefaultBridge"]; !ok { +++ gData["AllowNonDefaultBridge"] = "true" +++ } +++ nc.Options[netlabel.GenericData] = genericData +++ } +++} +++ +++/*************************** +++ NetworkController interface +++****************************/ +++func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ var create networkCreate +++ +++ err := json.Unmarshal(body, &create) +++ if err != nil { +++ return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} +++ } +++ processCreateDefaults(c, &create) +++ +++ nw, err := c.NewNetwork(create.NetworkType, create.Name, create.parseOptions()...) +++ if err != nil { +++ return "", convertNetworkError(err) +++ } +++ +++ return nw.ID(), &createdResponse +++} +++ +++func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ t, by := detectNetworkTarget(vars) +++ nw, errRsp := findNetwork(c, t, by) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ return buildNetworkResource(nw), &successResponse +++} +++ +++func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ var list []*networkResource +++ +++ // Look for query filters and validate +++ name, queryByName := vars[urlNwName] +++ shortID, queryByPid := vars[urlNwPID] +++ if queryByName && queryByPid { +++ return nil, &badQueryResponse +++ } +++ +++ if queryByName { +++ if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() { +++ list = append(list, buildNetworkResource(nw)) +++ } +++ } else if queryByPid { +++ // Return all the prefix-matching networks +++ l := func(nw libnetwork.Network) bool { +++ if strings.HasPrefix(nw.ID(), shortID) { +++ list = append(list, buildNetworkResource(nw)) +++ } +++ return false +++ } +++ c.WalkNetworks(l) +++ } else { +++ for _, nw := range c.Networks() { +++ list = append(list, buildNetworkResource(nw)) +++ } +++ } +++ +++ return list, &successResponse +++} +++ +++/****************** +++ Network interface +++*******************/ +++func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ var ec endpointCreate +++ +++ err := json.Unmarshal(body, &ec) +++ if err != nil { +++ return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} +++ } +++ +++ nwT, nwBy := detectNetworkTarget(vars) +++ n, errRsp := findNetwork(c, nwT, nwBy) +++ if !errRsp.isOK() { +++ return "", errRsp +++ } +++ +++ var setFctList []libnetwork.EndpointOption +++ if ec.ExposedPorts != nil { +++ setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts)) +++ } +++ if ec.PortMapping != nil { +++ setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping)) +++ } +++ +++ ep, err := n.CreateEndpoint(ec.Name, setFctList...) +++ if err != nil { +++ return "", convertNetworkError(err) +++ } +++ +++ return ep.ID(), &createdResponse +++} +++ +++func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ nwT, nwBy := detectNetworkTarget(vars) +++ epT, epBy := detectEndpointTarget(vars) +++ +++ ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ +++ return buildEndpointResource(ep), &successResponse +++} +++ +++func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ // Look for query filters and validate +++ name, queryByName := vars[urlEpName] +++ shortID, queryByPid := vars[urlEpPID] +++ if queryByName && queryByPid { +++ return nil, &badQueryResponse +++ } +++ +++ nwT, nwBy := detectNetworkTarget(vars) +++ nw, errRsp := findNetwork(c, nwT, nwBy) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ +++ var list []*endpointResource +++ +++ // If query parameter is specified, return a filtered collection +++ if queryByName { +++ if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() { +++ list = append(list, buildEndpointResource(ep)) +++ } +++ } else if queryByPid { +++ // Return all the prefix-matching endpoints +++ l := func(ep libnetwork.Endpoint) bool { +++ if strings.HasPrefix(ep.ID(), shortID) { +++ list = append(list, buildEndpointResource(ep)) +++ } +++ return false +++ } +++ nw.WalkEndpoints(l) +++ } else { +++ for _, ep := range nw.Endpoints() { +++ epr := buildEndpointResource(ep) +++ list = append(list, epr) +++ } +++ } +++ +++ return list, &successResponse +++} +++ +++func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ target, by := detectNetworkTarget(vars) +++ +++ nw, errRsp := findNetwork(c, target, by) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ +++ err := nw.Delete() +++ if err != nil { +++ return nil, convertNetworkError(err) +++ } +++ +++ return nil, &successResponse +++} +++ +++/****************** +++ Endpoint interface +++*******************/ +++func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ var ej endpointJoin +++ err := json.Unmarshal(body, &ej) +++ if err != nil { +++ return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} +++ } +++ +++ nwT, nwBy := detectNetworkTarget(vars) +++ epT, epBy := detectEndpointTarget(vars) +++ +++ ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ +++ err = ep.Join(ej.ContainerID, ej.parseOptions()...) +++ if err != nil { +++ return nil, convertNetworkError(err) +++ } +++ return ep.Info().SandboxKey(), &successResponse +++} +++ +++func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ nwT, nwBy := detectNetworkTarget(vars) +++ epT, epBy := detectEndpointTarget(vars) +++ +++ ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ +++ err := ep.Leave(vars[urlCnID]) +++ if err != nil { +++ return nil, convertNetworkError(err) +++ } +++ +++ return nil, &successResponse +++} +++ +++func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ nwT, nwBy := detectNetworkTarget(vars) +++ epT, epBy := detectEndpointTarget(vars) +++ +++ ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ +++ err := ep.Delete() +++ if err != nil { +++ return nil, convertNetworkError(err) +++ } +++ +++ return nil, &successResponse +++} +++ +++/****************** +++ Service interface +++*******************/ +++func procGetServices(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ // Look for query filters and validate +++ nwName, filterByNwName := vars[urlNwName] +++ svName, queryBySvName := vars[urlEpName] +++ shortID, queryBySvPID := vars[urlEpPID] +++ +++ if filterByNwName && queryBySvName || filterByNwName && queryBySvPID || queryBySvName && queryBySvPID { +++ return nil, &badQueryResponse +++ } +++ +++ var list []*endpointResource +++ +++ switch { +++ case filterByNwName: +++ // return all service present on the specified network +++ nw, errRsp := findNetwork(c, nwName, byName) +++ if !errRsp.isOK() { +++ return list, &successResponse +++ } +++ for _, ep := range nw.Endpoints() { +++ epr := buildEndpointResource(ep) +++ list = append(list, epr) +++ } +++ case queryBySvName: +++ // Look in each network for the service with the specified name +++ l := func(ep libnetwork.Endpoint) bool { +++ if ep.Name() == svName { +++ list = append(list, buildEndpointResource(ep)) +++ return true +++ } +++ return false +++ } +++ for _, nw := range c.Networks() { +++ nw.WalkEndpoints(l) +++ } +++ case queryBySvPID: +++ // Return all the prefix-matching services +++ l := func(ep libnetwork.Endpoint) bool { +++ if strings.HasPrefix(ep.ID(), shortID) { +++ list = append(list, buildEndpointResource(ep)) +++ } +++ return false +++ } +++ for _, nw := range c.Networks() { +++ nw.WalkEndpoints(l) +++ } +++ default: +++ for _, nw := range c.Networks() { +++ for _, ep := range nw.Endpoints() { +++ epr := buildEndpointResource(ep) +++ list = append(list, epr) +++ } +++ } +++ } +++ +++ return list, &successResponse +++} +++ +++func procGetService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ epT, epBy := detectEndpointTarget(vars) +++ sv, errRsp := findService(c, epT, epBy) +++ if !errRsp.isOK() { +++ return nil, endpointToService(errRsp) +++ } +++ return buildEndpointResource(sv), &successResponse +++} +++ +++func procGetContainers(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ epT, epBy := detectEndpointTarget(vars) +++ sv, errRsp := findService(c, epT, epBy) +++ if !errRsp.isOK() { +++ return nil, endpointToService(errRsp) +++ } +++ var list []*containerResource +++ if sv.ContainerInfo() != nil { +++ list = append(list, buildContainerResource(sv.ContainerInfo())) +++ } +++ return list, &successResponse +++} +++ +++func procPublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ var sp servicePublish +++ +++ err := json.Unmarshal(body, &sp) +++ if err != nil { +++ return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} +++ } +++ +++ n, errRsp := findNetwork(c, sp.Network, byName) +++ if !errRsp.isOK() { +++ return "", errRsp +++ } +++ +++ var setFctList []libnetwork.EndpointOption +++ if sp.ExposedPorts != nil { +++ setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(sp.ExposedPorts)) +++ } +++ if sp.PortMapping != nil { +++ setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(sp.PortMapping)) +++ } +++ +++ ep, err := n.CreateEndpoint(sp.Name, setFctList...) +++ if err != nil { +++ return "", endpointToService(convertNetworkError(err)) +++ } +++ +++ return ep.ID(), &createdResponse +++} +++ +++func procUnpublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ epT, epBy := detectEndpointTarget(vars) +++ sv, errRsp := findService(c, epT, epBy) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ err := sv.Delete() +++ if err != nil { +++ return nil, endpointToService(convertNetworkError(err)) +++ } +++ return nil, &successResponse +++} +++ +++func procAttachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ var bk endpointJoin +++ err := json.Unmarshal(body, &bk) +++ if err != nil { +++ return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} +++ } +++ +++ epT, epBy := detectEndpointTarget(vars) +++ sv, errRsp := findService(c, epT, epBy) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ +++ err = sv.Join(bk.ContainerID, bk.parseOptions()...) +++ if err != nil { +++ return nil, convertNetworkError(err) +++ } +++ return sv.Info().SandboxKey(), &successResponse +++} +++ +++func procDetachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { +++ epT, epBy := detectEndpointTarget(vars) +++ sv, errRsp := findService(c, epT, epBy) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ +++ err := sv.Leave(vars[urlCnID]) +++ if err != nil { +++ return nil, convertNetworkError(err) +++ } +++ +++ return nil, &successResponse +++} +++ +++/*********** +++ Utilities +++************/ +++const ( +++ byID = iota +++ byName +++) +++ +++func detectNetworkTarget(vars map[string]string) (string, int) { +++ if target, ok := vars[urlNwName]; ok { +++ return target, byName +++ } +++ if target, ok := vars[urlNwID]; ok { +++ return target, byID +++ } +++ // vars are populated from the URL, following cannot happen +++ panic("Missing URL variable parameter for network") +++} +++ +++func detectEndpointTarget(vars map[string]string) (string, int) { +++ if target, ok := vars[urlEpName]; ok { +++ return target, byName +++ } +++ if target, ok := vars[urlEpID]; ok { +++ return target, byID +++ } +++ // vars are populated from the URL, following cannot happen +++ panic("Missing URL variable parameter for endpoint") +++} +++ +++func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) { +++ var ( +++ nw libnetwork.Network +++ err error +++ ) +++ switch by { +++ case byID: +++ nw, err = c.NetworkByID(s) +++ case byName: +++ if s == "" { +++ s = c.Config().Daemon.DefaultNetwork +++ } +++ nw, err = c.NetworkByName(s) +++ default: +++ panic(fmt.Sprintf("unexpected selector for network search: %d", by)) +++ } +++ if err != nil { +++ if _, ok := err.(types.NotFoundError); ok { +++ return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound} +++ } +++ return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} +++ } +++ return nw, &successResponse +++} +++ +++func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) { +++ nw, errRsp := findNetwork(c, ns, nwBy) +++ if !errRsp.isOK() { +++ return nil, errRsp +++ } +++ var ( +++ err error +++ ep libnetwork.Endpoint +++ ) +++ switch epBy { +++ case byID: +++ ep, err = nw.EndpointByID(es) +++ case byName: +++ ep, err = nw.EndpointByName(es) +++ default: +++ panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy)) +++ } +++ if err != nil { +++ if _, ok := err.(types.NotFoundError); ok { +++ return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound} +++ } +++ return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} +++ } +++ return ep, &successResponse +++} +++ +++func findService(c libnetwork.NetworkController, svs string, svBy int) (libnetwork.Endpoint, *responseStatus) { +++ for _, nw := range c.Networks() { +++ var ( +++ ep libnetwork.Endpoint +++ err error +++ ) +++ switch svBy { +++ case byID: +++ ep, err = nw.EndpointByID(svs) +++ case byName: +++ ep, err = nw.EndpointByName(svs) +++ default: +++ panic(fmt.Sprintf("unexpected selector for service search: %d", svBy)) +++ } +++ if err == nil { +++ return ep, &successResponse +++ } else if _, ok := err.(types.NotFoundError); !ok { +++ return nil, convertNetworkError(err) +++ } +++ } +++ return nil, &responseStatus{Status: "Service not found", StatusCode: http.StatusNotFound} +++} +++ +++func endpointToService(rsp *responseStatus) *responseStatus { +++ rsp.Status = strings.Replace(rsp.Status, "endpoint", "service", -1) +++ return rsp +++} +++ +++func convertNetworkError(err error) *responseStatus { +++ var code int +++ switch err.(type) { +++ case types.BadRequestError: +++ code = http.StatusBadRequest +++ case types.ForbiddenError: +++ code = http.StatusForbidden +++ case types.NotFoundError: +++ code = http.StatusNotFound +++ case types.TimeoutError: +++ code = http.StatusRequestTimeout +++ case types.NotImplementedError: +++ code = http.StatusNotImplemented +++ case types.NoServiceError: +++ code = http.StatusServiceUnavailable +++ case types.InternalError: +++ code = http.StatusInternalServerError +++ default: +++ code = http.StatusInternalServerError +++ } +++ return &responseStatus{Status: err.Error(), StatusCode: code} +++} +++ +++func writeJSON(w http.ResponseWriter, code int, v interface{}) error { +++ w.Header().Set("Content-Type", "application/json") +++ w.WriteHeader(code) +++ return json.NewEncoder(w).Encode(v) +++} diff --cc libnetwork/api/api_test.go index 00000000,00000000,00000000..9916747c new file mode 100644 --- /dev/null +++ b/libnetwork/api/api_test.go @@@@ -1,0 -1,0 -1,0 +1,2130 @@@@ +++package api +++ +++import ( +++ "bytes" +++ "encoding/json" +++ "errors" +++ "fmt" +++ "io" +++ "net/http" +++ "os" +++ "runtime" +++ "testing" +++ +++ "github.com/docker/docker/pkg/reexec" +++ "github.com/docker/libnetwork" +++ "github.com/docker/libnetwork/netlabel" +++ "github.com/docker/libnetwork/options" +++ "github.com/docker/libnetwork/sandbox" +++ "github.com/docker/libnetwork/types" +++) +++ +++const ( +++ bridgeNetType = "bridge" +++ bridgeName = "docker0" +++) +++ +++func getEmptyGenericOption() map[string]interface{} { +++ genericOption := make(map[string]interface{}) +++ genericOption[netlabel.GenericData] = options.Generic{} +++ return genericOption +++} +++ +++func i2s(i interface{}) string { +++ s, ok := i.(string) +++ if !ok { +++ panic(fmt.Sprintf("Failed i2s for %v", i)) +++ } +++ return s +++} +++ +++func i2e(i interface{}) *endpointResource { +++ s, ok := i.(*endpointResource) +++ if !ok { +++ panic(fmt.Sprintf("Failed i2e for %v", i)) +++ } +++ return s +++} +++ +++func i2eL(i interface{}) []*endpointResource { +++ s, ok := i.([]*endpointResource) +++ if !ok { +++ panic(fmt.Sprintf("Failed i2eL for %v", i)) +++ } +++ return s +++} +++ +++func i2n(i interface{}) *networkResource { +++ s, ok := i.(*networkResource) +++ if !ok { +++ panic(fmt.Sprintf("Failed i2n for %v", i)) +++ } +++ return s +++} +++ +++func i2nL(i interface{}) []*networkResource { +++ s, ok := i.([]*networkResource) +++ if !ok { +++ panic(fmt.Sprintf("Failed i2nL for %v", i)) +++ } +++ return s +++} +++ +++func i2cL(i interface{}) []*containerResource { +++ s, ok := i.([]*containerResource) +++ if !ok { +++ panic(fmt.Sprintf("Failed i2cL for %v", i)) +++ } +++ return s +++} +++ +++func createTestNetwork(t *testing.T, network string) (libnetwork.NetworkController, libnetwork.Network) { +++ c, err := libnetwork.New() +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ err = c.ConfigureNetworkDriver(bridgeNetType, nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ netOption := options.Generic{ +++ netlabel.GenericData: options.Generic{ +++ "BridgeName": network, +++ "AllowNonDefaultBridge": true, +++ }, +++ } +++ netGeneric := libnetwork.NetworkOptionGeneric(netOption) +++ nw, err := c.NewNetwork(bridgeNetType, network, netGeneric) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ return c, nw +++} +++ +++func TestMain(m *testing.M) { +++ if reexec.Init() { +++ return +++ } +++ os.Exit(m.Run()) +++} +++ +++func TestJoinOptionParser(t *testing.T) { +++ hn := "host1" +++ dn := "docker.com" +++ hp := "/etc/hosts" +++ rc := "/etc/resolv.conf" +++ dnss := []string{"8.8.8.8", "172.28.34.5"} +++ ehs := []endpointExtraHost{endpointExtraHost{Name: "extra1", Address: "172.28.9.1"}, endpointExtraHost{Name: "extra2", Address: "172.28.9.2"}} +++ pus := []endpointParentUpdate{endpointParentUpdate{EndpointID: "abc123def456", Name: "serv1", Address: "172.28.30.123"}} +++ +++ ej := endpointJoin{ +++ HostName: hn, +++ DomainName: dn, +++ HostsPath: hp, +++ ResolvConfPath: rc, +++ DNS: dnss, +++ ExtraHosts: ehs, +++ ParentUpdates: pus, +++ UseDefaultSandbox: true, +++ } +++ +++ if len(ej.parseOptions()) != 10 { +++ t.Fatalf("Failed to generate all libnetwork.EndpointJoinOption methods libnetwork.EndpointJoinOption method") +++ } +++ +++} +++ +++func TestJson(t *testing.T) { +++ nc := networkCreate{NetworkType: bridgeNetType} +++ b, err := json.Marshal(nc) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ var ncp networkCreate +++ err = json.Unmarshal(b, &ncp) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ if nc.NetworkType != ncp.NetworkType { +++ t.Fatalf("Incorrect networkCreate after json encoding/deconding: %v", ncp) +++ } +++ +++ jl := endpointJoin{ContainerID: "abcdef456789"} +++ b, err = json.Marshal(jl) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ var jld endpointJoin +++ err = json.Unmarshal(b, &jld) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ if jl.ContainerID != jld.ContainerID { +++ t.Fatalf("Incorrect endpointJoin after json encoding/deconding: %v", jld) +++ } +++} +++ +++func TestCreateDeleteNetwork(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, err := libnetwork.New() +++ if err != nil { +++ t.Fatal(err) +++ } +++ err = c.ConfigureNetworkDriver(bridgeNetType, nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ badBody, err := json.Marshal("bad body") +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars := make(map[string]string) +++ _, errRsp := procCreateNetwork(c, nil, badBody) +++ if errRsp == &createdResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp) +++ } +++ +++ incompleteBody, err := json.Marshal(networkCreate{}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ _, errRsp = procCreateNetwork(c, vars, incompleteBody) +++ if errRsp == &createdResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp) +++ } +++ +++ ops := options.Generic{ +++ netlabel.EnableIPv6: true, +++ netlabel.GenericData: map[string]string{ +++ "BridgeName": "abc", +++ "AllowNonDefaultBridge": "true", +++ "FixedCIDRv6": "fe80::1/64", +++ "AddressIP": "172.28.30.254/24", +++ }, +++ } +++ nc := networkCreate{Name: "network_1", NetworkType: bridgeNetType, Options: ops} +++ goodBody, err := json.Marshal(nc) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ _, errRsp = procCreateNetwork(c, vars, goodBody) +++ if errRsp != &createdResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ vars[urlNwName] = "" +++ _, errRsp = procDeleteNetwork(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ +++ vars[urlNwName] = "abc" +++ _, errRsp = procDeleteNetwork(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ +++ vars[urlNwName] = "network_1" +++ _, errRsp = procDeleteNetwork(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++} +++ +++func TestGetNetworksAndEndpoints(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, err := libnetwork.New() +++ if err != nil { +++ t.Fatal(err) +++ } +++ err = c.ConfigureNetworkDriver(bridgeNetType, nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ ops := options.Generic{ +++ netlabel.GenericData: map[string]string{ +++ "BridgeName": "api_test_nw", +++ "AllowNonDefaultBridge": "true", +++ }, +++ } +++ +++ nc := networkCreate{Name: "sh", NetworkType: bridgeNetType, Options: ops} +++ body, err := json.Marshal(nc) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars := make(map[string]string) +++ inid, errRsp := procCreateNetwork(c, vars, body) +++ if errRsp != &createdResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ nid, ok := inid.(string) +++ if !ok { +++ t.FailNow() +++ } +++ +++ ec1 := endpointCreate{ +++ Name: "ep1", +++ ExposedPorts: []types.TransportPort{ +++ types.TransportPort{Proto: types.TCP, Port: uint16(5000)}, +++ types.TransportPort{Proto: types.UDP, Port: uint16(400)}, +++ types.TransportPort{Proto: types.TCP, Port: uint16(600)}, +++ }, +++ PortMapping: []types.PortBinding{ +++ types.PortBinding{Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)}, +++ types.PortBinding{Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)}, +++ types.PortBinding{Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)}, +++ }, +++ } +++ b1, err := json.Marshal(ec1) +++ if err != nil { +++ t.Fatal(err) +++ } +++ ec2 := endpointCreate{Name: "ep2"} +++ b2, err := json.Marshal(ec2) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars[urlNwName] = "sh" +++ vars[urlEpName] = "ep1" +++ ieid1, errRsp := procCreateEndpoint(c, vars, b1) +++ if errRsp != &createdResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ eid1 := i2s(ieid1) +++ vars[urlEpName] = "ep2" +++ ieid2, errRsp := procCreateEndpoint(c, vars, b2) +++ if errRsp != &createdResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ eid2 := i2s(ieid2) +++ +++ vars[urlNwName] = "" +++ vars[urlEpName] = "ep1" +++ _, errRsp = procGetEndpoint(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure but succeeded: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected to fail with http.StatusBadRequest, but got: %d", errRsp.StatusCode) +++ } +++ +++ vars = make(map[string]string) +++ vars[urlNwName] = "sh" +++ vars[urlEpID] = "" +++ _, errRsp = procGetEndpoint(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure but succeeded: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected to fail with http.StatusBadRequest, but got: %d", errRsp.StatusCode) +++ } +++ +++ vars = make(map[string]string) +++ vars[urlNwID] = "" +++ vars[urlEpID] = eid1 +++ _, errRsp = procGetEndpoint(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure but succeeded: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected to fail with http.StatusBadRequest, but got: %d", errRsp.StatusCode) +++ } +++ +++ // nw by name and ep by id +++ vars[urlNwName] = "sh" +++ i1, errRsp := procGetEndpoint(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ // nw by name and ep by name +++ delete(vars, urlEpID) +++ vars[urlEpName] = "ep1" +++ i2, errRsp := procGetEndpoint(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ // nw by id and ep by name +++ delete(vars, urlNwName) +++ vars[urlNwID] = nid +++ i3, errRsp := procGetEndpoint(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ // nw by id and ep by id +++ delete(vars, urlEpName) +++ vars[urlEpID] = eid1 +++ i4, errRsp := procGetEndpoint(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ id1 := i2e(i1).ID +++ if id1 != i2e(i2).ID || id1 != i2e(i3).ID || id1 != i2e(i4).ID { +++ t.Fatalf("Endpoints retireved via different query parameters differ: %v, %v, %v, %v", i1, i2, i3, i4) +++ } +++ +++ vars[urlNwName] = "" +++ _, errRsp = procGetEndpoints(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ delete(vars, urlNwName) +++ vars[urlNwID] = "fakeID" +++ _, errRsp = procGetEndpoints(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlNwID] = nid +++ _, errRsp = procGetEndpoints(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ vars[urlNwName] = "sh" +++ iepList, errRsp := procGetEndpoints(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ epList := i2eL(iepList) +++ if len(epList) != 2 { +++ t.Fatalf("Did not return the expected number (2) of endpoint resources: %d", len(epList)) +++ } +++ if "sh" != epList[0].Network || "sh" != epList[1].Network { +++ t.Fatalf("Did not find expected network name in endpoint resources") +++ } +++ +++ vars = make(map[string]string) +++ vars[urlNwName] = "" +++ _, errRsp = procGetNetwork(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Exepected failure, got: %v", errRsp) +++ } +++ vars[urlNwName] = "shhhhh" +++ _, errRsp = procGetNetwork(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Exepected failure, got: %v", errRsp) +++ } +++ vars[urlNwName] = "sh" +++ inr1, errRsp := procGetNetwork(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ nr1 := i2n(inr1) +++ +++ delete(vars, urlNwName) +++ vars[urlNwID] = "cacca" +++ _, errRsp = procGetNetwork(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ vars[urlNwID] = nid +++ inr2, errRsp := procGetNetwork(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("procgetNetworkByName() != procgetNetworkById(), %v vs %v", inr1, inr2) +++ } +++ nr2 := i2n(inr2) +++ if nr1.Name != nr2.Name || nr1.Type != nr2.Type || nr1.ID != nr2.ID || len(nr1.Endpoints) != len(nr2.Endpoints) { +++ t.Fatalf("Get by name and Get failure: %v", errRsp) +++ } +++ +++ if len(nr1.Endpoints) != 2 { +++ t.Fatalf("Did not find the expected number (2) of endpoint resources in the network resource: %d", len(nr1.Endpoints)) +++ } +++ for _, er := range nr1.Endpoints { +++ if er.ID != eid1 && er.ID != eid2 { +++ t.Fatalf("Did not find the expected endpoint resources in the network resource: %v", nr1.Endpoints) +++ } +++ } +++ +++ iList, errRsp := procGetNetworks(c, nil, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ netList := i2nL(iList) +++ if len(netList) != 1 { +++ t.Fatalf("Did not return the expected number of network resources") +++ } +++ if nid != netList[0].ID { +++ t.Fatalf("Did not find expected network %s: %v", nid, netList) +++ } +++ +++ _, errRsp = procDeleteNetwork(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Exepected failure, got: %v", errRsp) +++ } +++ +++ vars[urlEpName] = "ep1" +++ _, errRsp = procDeleteEndpoint(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ delete(vars, urlEpName) +++ iepList, errRsp = procGetEndpoints(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ epList = i2eL(iepList) +++ if len(epList) != 1 { +++ t.Fatalf("Did not return the expected number (1) of endpoint resources: %d", len(epList)) +++ } +++ +++ vars[urlEpName] = "ep2" +++ _, errRsp = procDeleteEndpoint(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ iepList, errRsp = procGetEndpoints(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ epList = i2eL(iepList) +++ if len(epList) != 0 { +++ t.Fatalf("Did not return the expected number (0) of endpoint resources: %d", len(epList)) +++ } +++ +++ _, errRsp = procDeleteNetwork(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ iList, errRsp = procGetNetworks(c, nil, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ netList = i2nL(iList) +++ if len(netList) != 0 { +++ t.Fatalf("Did not return the expected number of network resources") +++ } +++} +++ +++func TestProcGetServices(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, err := libnetwork.New() +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ err = c.ConfigureNetworkDriver(bridgeNetType, nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ // Create 2 networks +++ netName1 := "production" +++ netOption := options.Generic{ +++ netlabel.GenericData: options.Generic{ +++ "BridgeName": netName1, +++ "AllowNonDefaultBridge": true, +++ }, +++ } +++ nw1, err := c.NewNetwork(bridgeNetType, netName1, libnetwork.NetworkOptionGeneric(netOption)) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ netName2 := "work-dev" +++ netOption = options.Generic{ +++ netlabel.GenericData: options.Generic{ +++ "BridgeName": netName2, +++ "AllowNonDefaultBridge": true, +++ }, +++ } +++ nw2, err := c.NewNetwork(bridgeNetType, netName2, libnetwork.NetworkOptionGeneric(netOption)) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars := make(map[string]string) +++ li, errRsp := procGetServices(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ list := i2eL(li) +++ if len(list) != 0 { +++ t.Fatalf("Unexpected services in response: %v", list) +++ } +++ +++ // Add a couple of services on one network and one on the other network +++ ep11, err := nw1.CreateEndpoint("db-prod") +++ if err != nil { +++ t.Fatal(err) +++ } +++ ep12, err := nw1.CreateEndpoint("web-prod") +++ if err != nil { +++ t.Fatal(err) +++ } +++ ep21, err := nw2.CreateEndpoint("db-dev") +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ li, errRsp = procGetServices(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ list = i2eL(li) +++ if len(list) != 3 { +++ t.Fatalf("Unexpected services in response: %v", list) +++ } +++ +++ // Filter by network +++ vars[urlNwName] = netName1 +++ li, errRsp = procGetServices(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ list = i2eL(li) +++ if len(list) != 2 { +++ t.Fatalf("Unexpected services in response: %v", list) +++ } +++ +++ vars[urlNwName] = netName2 +++ li, errRsp = procGetServices(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ list = i2eL(li) +++ if len(list) != 1 { +++ t.Fatalf("Unexpected services in response: %v", list) +++ } +++ +++ vars[urlNwName] = "unknown-network" +++ li, errRsp = procGetServices(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ list = i2eL(li) +++ if len(list) != 0 { +++ t.Fatalf("Unexpected services in response: %v", list) +++ } +++ +++ // Query by name +++ delete(vars, urlNwName) +++ vars[urlEpName] = "db-prod" +++ li, errRsp = procGetServices(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ list = i2eL(li) +++ if len(list) != 1 { +++ t.Fatalf("Unexpected services in response: %v", list) +++ } +++ +++ vars[urlEpName] = "no-service" +++ li, errRsp = procGetServices(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ list = i2eL(li) +++ if len(list) != 0 { +++ t.Fatalf("Unexpected services in response: %v", list) +++ } +++ +++ // Query by id or partial id +++ delete(vars, urlEpName) +++ vars[urlEpPID] = ep12.ID() +++ li, errRsp = procGetServices(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ list = i2eL(li) +++ if len(list) != 1 { +++ t.Fatalf("Unexpected services in response: %v", list) +++ } +++ if list[0].ID != ep12.ID() { +++ t.Fatalf("Unexpected element in response: %v", list) +++ } +++ +++ vars[urlEpPID] = "non-id" +++ li, errRsp = procGetServices(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ list = i2eL(li) +++ if len(list) != 0 { +++ t.Fatalf("Unexpected services in response: %v", list) +++ } +++ +++ delete(vars, urlEpPID) +++ err = ep11.Delete() +++ if err != nil { +++ t.Fatal(err) +++ } +++ err = ep12.Delete() +++ if err != nil { +++ t.Fatal(err) +++ } +++ err = ep21.Delete() +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ li, errRsp = procGetServices(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ list = i2eL(li) +++ if len(list) != 0 { +++ t.Fatalf("Unexpected services in response: %v", list) +++ } +++} +++ +++func TestProcGetService(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, nw := createTestNetwork(t, "network") +++ ep1, err := nw.CreateEndpoint("db") +++ if err != nil { +++ t.Fatal(err) +++ } +++ ep2, err := nw.CreateEndpoint("web") +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars := map[string]string{urlEpID: ""} +++ _, errRsp := procGetService(c, vars, nil) +++ if errRsp.isOK() { +++ t.Fatalf("Expected failure, but suceeded") +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d, but got: %d", http.StatusBadRequest, errRsp.StatusCode) +++ } +++ +++ vars[urlEpID] = "unknown-service-id" +++ _, errRsp = procGetService(c, vars, nil) +++ if errRsp.isOK() { +++ t.Fatalf("Expected failure, but suceeded") +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d, but got: %d. (%v)", http.StatusNotFound, errRsp.StatusCode, errRsp) +++ } +++ +++ vars[urlEpID] = ep1.ID() +++ si, errRsp := procGetService(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ sv := i2e(si) +++ if sv.ID != ep1.ID() { +++ t.Fatalf("Unexpected service resource returned: %v", sv) +++ } +++ +++ vars[urlEpID] = ep2.ID() +++ si, errRsp = procGetService(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ sv = i2e(si) +++ if sv.ID != ep2.ID() { +++ t.Fatalf("Unexpected service resource returned: %v", sv) +++ } +++} +++ +++func TestProcPublishUnpublishService(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, _ := createTestNetwork(t, "network") +++ vars := make(map[string]string) +++ +++ vbad, err := json.Marshal("bad service create data") +++ if err != nil { +++ t.Fatal(err) +++ } +++ _, errRsp := procPublishService(c, vars, vbad) +++ if errRsp == &createdResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) +++ } +++ +++ b, err := json.Marshal(servicePublish{Name: ""}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ _, errRsp = procPublishService(c, vars, b) +++ if errRsp == &createdResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) +++ } +++ +++ b, err = json.Marshal(servicePublish{Name: "db"}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ _, errRsp = procPublishService(c, vars, b) +++ if errRsp == &createdResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) +++ } +++ +++ b, err = json.Marshal(servicePublish{Name: "db", Network: "unknown-network"}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ _, errRsp = procPublishService(c, vars, b) +++ if errRsp == &createdResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d. Got: %v", http.StatusNotFound, errRsp) +++ } +++ +++ b, err = json.Marshal(servicePublish{Name: "", Network: "network"}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ _, errRsp = procPublishService(c, vars, b) +++ if errRsp == &createdResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) +++ } +++ +++ b, err = json.Marshal(servicePublish{Name: "db", Network: "network"}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ _, errRsp = procPublishService(c, vars, b) +++ if errRsp != &createdResponse { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ +++ sp := servicePublish{ +++ Name: "web", +++ Network: "network", +++ ExposedPorts: []types.TransportPort{ +++ types.TransportPort{Proto: types.TCP, Port: uint16(6000)}, +++ types.TransportPort{Proto: types.UDP, Port: uint16(500)}, +++ types.TransportPort{Proto: types.TCP, Port: uint16(700)}, +++ }, +++ PortMapping: []types.PortBinding{ +++ types.PortBinding{Proto: types.TCP, Port: uint16(1230), HostPort: uint16(37000)}, +++ types.PortBinding{Proto: types.UDP, Port: uint16(1200), HostPort: uint16(36000)}, +++ types.PortBinding{Proto: types.TCP, Port: uint16(1120), HostPort: uint16(35000)}, +++ }, +++ } +++ b, err = json.Marshal(sp) +++ if err != nil { +++ t.Fatal(err) +++ } +++ si, errRsp := procPublishService(c, vars, b) +++ if errRsp != &createdResponse { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ sid := i2s(si) +++ +++ vars[urlEpID] = "" +++ _, errRsp = procUnpublishService(c, vars, nil) +++ if errRsp.isOK() { +++ t.Fatalf("Expected failure but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) +++ } +++ +++ vars[urlEpID] = "unknown-service-id" +++ _, errRsp = procUnpublishService(c, vars, nil) +++ if errRsp.isOK() { +++ t.Fatalf("Expected failure but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d. Got: %v", http.StatusNotFound, errRsp) +++ } +++ +++ vars[urlEpID] = sid +++ _, errRsp = procUnpublishService(c, vars, nil) +++ if !errRsp.isOK() { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ +++ _, errRsp = procGetService(c, vars, nil) +++ if errRsp.isOK() { +++ t.Fatalf("Expected failure, but suceeded") +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d, but got: %d. (%v)", http.StatusNotFound, errRsp.StatusCode, errRsp) +++ } +++} +++ +++func TestAttachDetachBackend(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, nw := createTestNetwork(t, "network") +++ ep1, err := nw.CreateEndpoint("db") +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars := make(map[string]string) +++ +++ vbad, err := json.Marshal("bad data") +++ if err != nil { +++ t.Fatal(err) +++ } +++ _, errRsp := procAttachBackend(c, vars, vbad) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlEpName] = "endpoint" +++ bad, err := json.Marshal(endpointJoin{}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ _, errRsp = procAttachBackend(c, vars, bad) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d. Got: %v", http.StatusNotFound, errRsp) +++ } +++ +++ _, errRsp = procGetContainers(c, vars, nil) +++ if errRsp.isOK() { +++ t.Fatalf("Expected failure. Got %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d. Got: %v", http.StatusNotFound, errRsp) +++ } +++ +++ vars[urlEpName] = "db" +++ _, errRsp = procAttachBackend(c, vars, bad) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) +++ } +++ +++ cid := "abcdefghi" +++ jl := endpointJoin{ContainerID: cid} +++ jlb, err := json.Marshal(jl) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ _, errRsp = procAttachBackend(c, vars, jlb) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexpected failure, got: %v", errRsp) +++ } +++ +++ cli, errRsp := procGetContainers(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexpected failure, got: %v", errRsp) +++ } +++ cl := i2cL(cli) +++ if len(cl) != 1 { +++ t.Fatalf("Did not find expected number of containers attached to the service: %d", len(cl)) +++ } +++ if cl[0].ID != cid { +++ t.Fatalf("Did not find expected container attached to the service: %v", cl[0]) +++ } +++ +++ _, errRsp = procUnpublishService(c, vars, nil) +++ if errRsp.isOK() { +++ t.Fatalf("Expected failure but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusForbidden { +++ t.Fatalf("Expected %d. Got: %v", http.StatusForbidden, errRsp) +++ } +++ +++ vars[urlEpName] = "endpoint" +++ _, errRsp = procDetachBackend(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d. Got: %v", http.StatusNotFound, errRsp) +++ } +++ +++ vars[urlEpName] = "db" +++ _, errRsp = procDetachBackend(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) +++ } +++ +++ vars[urlCnID] = cid +++ _, errRsp = procDetachBackend(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexpected failure, got: %v", errRsp) +++ } +++ +++ cli, errRsp = procGetContainers(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexpected failure, got: %v", errRsp) +++ } +++ cl = i2cL(cli) +++ if len(cl) != 0 { +++ t.Fatalf("Did not find expected number of containers attached to the service: %d", len(cl)) +++ } +++ +++ err = ep1.Delete() +++ if err != nil { +++ t.Fatal(err) +++ } +++} +++ +++func TestDetectGetNetworksInvalidQueryComposition(t *testing.T) { +++ c, err := libnetwork.New() +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars := map[string]string{urlNwName: "x", urlNwPID: "y"} +++ _, errRsp := procGetNetworks(c, vars, nil) +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) +++ } +++} +++ +++func TestDetectGetEndpointsInvalidQueryComposition(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, _ := createTestNetwork(t, "network") +++ +++ vars := map[string]string{urlNwName: "network", urlEpName: "x", urlEpPID: "y"} +++ _, errRsp := procGetEndpoints(c, vars, nil) +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) +++ } +++} +++ +++func TestDetectGetServicesInvalidQueryComposition(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, _ := createTestNetwork(t, "network") +++ +++ vars := map[string]string{urlNwName: "network", urlEpName: "x", urlEpPID: "y"} +++ _, errRsp := procGetServices(c, vars, nil) +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) +++ } +++} +++ +++func TestFindNetworkUtilPanic(t *testing.T) { +++ defer checkPanic(t) +++ findNetwork(nil, "", -1) +++} +++ +++func TestFindNetworkUtil(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, nw := createTestNetwork(t, "network") +++ nid := nw.ID() +++ +++ _, errRsp := findNetwork(c, "", byName) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d, but got: %d", http.StatusBadRequest, errRsp.StatusCode) +++ } +++ +++ n, errRsp := findNetwork(c, nid, byID) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ if n == nil { +++ t.Fatalf("Unexpected nil libnetwork.Network") +++ } +++ if nid != n.ID() { +++ t.Fatalf("Incorrect libnetwork.Network resource. It has different id: %v", n) +++ } +++ if "network" != n.Name() { +++ t.Fatalf("Incorrect libnetwork.Network resource. It has different name: %v", n) +++ } +++ +++ n, errRsp = findNetwork(c, "network", byName) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexpected failure: %v", errRsp) +++ } +++ if n == nil { +++ t.Fatalf("Unexpected nil libnetwork.Network") +++ } +++ if nid != n.ID() { +++ t.Fatalf("Incorrect libnetwork.Network resource. It has different id: %v", n) +++ } +++ if "network" != n.Name() { +++ t.Fatalf("Incorrect libnetwork.Network resource. It has different name: %v", n) +++ } +++ +++ if err := n.Delete(); err != nil { +++ t.Fatalf("Failed to delete the network: %s", err.Error()) +++ } +++ +++ _, errRsp = findNetwork(c, nid, byID) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) +++ } +++ +++ _, errRsp = findNetwork(c, "network", byName) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) +++ } +++} +++ +++func TestCreateDeleteEndpoints(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, err := libnetwork.New() +++ if err != nil { +++ t.Fatal(err) +++ } +++ err = c.ConfigureNetworkDriver(bridgeNetType, nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ nc := networkCreate{Name: "firstNet", NetworkType: bridgeNetType} +++ body, err := json.Marshal(nc) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars := make(map[string]string) +++ i, errRsp := procCreateNetwork(c, vars, body) +++ if errRsp != &createdResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ nid := i2s(i) +++ +++ vbad, err := json.Marshal("bad endppoint create data") +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars[urlNwName] = "firstNet" +++ _, errRsp = procCreateEndpoint(c, vars, vbad) +++ if errRsp == &createdResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ +++ b, err := json.Marshal(endpointCreate{Name: ""}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars[urlNwName] = "secondNet" +++ _, errRsp = procCreateEndpoint(c, vars, b) +++ if errRsp == &createdResponse { +++ t.Fatalf("Expected to fail but succeeded") +++ } +++ +++ vars[urlNwName] = "firstNet" +++ _, errRsp = procCreateEndpoint(c, vars, b) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure but succeeded: %v", errRsp) +++ } +++ +++ b, err = json.Marshal(endpointCreate{Name: "firstEp"}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ i, errRsp = procCreateEndpoint(c, vars, b) +++ if errRsp != &createdResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ eid := i2s(i) +++ +++ _, errRsp = findEndpoint(c, "myNet", "firstEp", byName, byName) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure but succeeded: %v", errRsp) +++ } +++ +++ ep0, errRsp := findEndpoint(c, nid, "firstEp", byID, byName) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ ep1, errRsp := findEndpoint(c, "firstNet", "firstEp", byName, byName) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ ep2, errRsp := findEndpoint(c, nid, eid, byID, byID) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ ep3, errRsp := findEndpoint(c, "firstNet", eid, byName, byID) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ if ep0.ID() != ep1.ID() || ep0.ID() != ep2.ID() || ep0.ID() != ep3.ID() { +++ t.Fatalf("Diffenrent queries returned different endpoints: \nep0: %v\nep1: %v\nep2: %v\nep3: %v", ep0, ep1, ep2, ep3) +++ } +++ +++ vars = make(map[string]string) +++ vars[urlNwName] = "" +++ vars[urlEpName] = "ep1" +++ _, errRsp = procDeleteEndpoint(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlNwName] = "firstNet" +++ vars[urlEpName] = "" +++ _, errRsp = procDeleteEndpoint(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlEpName] = "ep2" +++ _, errRsp = procDeleteEndpoint(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlEpName] = "firstEp" +++ _, errRsp = procDeleteEndpoint(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ _, errRsp = findEndpoint(c, "firstNet", "firstEp", byName, byName) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++} +++ +++func TestJoinLeave(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, err := libnetwork.New() +++ if err != nil { +++ t.Fatal(err) +++ } +++ err = c.ConfigureNetworkDriver(bridgeNetType, nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ nb, err := json.Marshal(networkCreate{Name: "network", NetworkType: bridgeNetType}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ vars := make(map[string]string) +++ _, errRsp := procCreateNetwork(c, vars, nb) +++ if errRsp != &createdResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ eb, err := json.Marshal(endpointCreate{Name: "endpoint"}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ vars[urlNwName] = "network" +++ _, errRsp = procCreateEndpoint(c, vars, eb) +++ if errRsp != &createdResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ vbad, err := json.Marshal("bad data") +++ if err != nil { +++ t.Fatal(err) +++ } +++ _, errRsp = procJoinEndpoint(c, vars, vbad) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlEpName] = "endpoint" +++ bad, err := json.Marshal(endpointJoin{}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ _, errRsp = procJoinEndpoint(c, vars, bad) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ cid := "abcdefghi" +++ jl := endpointJoin{ContainerID: cid} +++ jlb, err := json.Marshal(jl) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ vars = make(map[string]string) +++ vars[urlNwName] = "" +++ vars[urlEpName] = "" +++ _, errRsp = procJoinEndpoint(c, vars, jlb) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlNwName] = "network" +++ vars[urlEpName] = "" +++ _, errRsp = procJoinEndpoint(c, vars, jlb) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlEpName] = "epoint" +++ _, errRsp = procJoinEndpoint(c, vars, jlb) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlEpName] = "endpoint" +++ key, errRsp := procJoinEndpoint(c, vars, jlb) +++ if errRsp != &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ keyStr := i2s(key) +++ if keyStr == "" { +++ t.Fatalf("Empty sandbox key") +++ } +++ _, errRsp = procDeleteEndpoint(c, vars, nil) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlNwName] = "network2" +++ _, errRsp = procLeaveEndpoint(c, vars, vbad) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ _, errRsp = procLeaveEndpoint(c, vars, bad) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ _, errRsp = procLeaveEndpoint(c, vars, jlb) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ vars = make(map[string]string) +++ vars[urlNwName] = "" +++ vars[urlEpName] = "" +++ _, errRsp = procLeaveEndpoint(c, vars, jlb) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ vars[urlNwName] = "network" +++ vars[urlEpName] = "" +++ _, errRsp = procLeaveEndpoint(c, vars, jlb) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ vars[urlEpName] = "2epoint" +++ _, errRsp = procLeaveEndpoint(c, vars, jlb) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ vars[urlEpName] = "epoint" +++ vars[urlCnID] = "who" +++ _, errRsp = procLeaveEndpoint(c, vars, jlb) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ delete(vars, urlCnID) +++ vars[urlEpName] = "endpoint" +++ _, errRsp = procLeaveEndpoint(c, vars, jlb) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ vars[urlCnID] = cid +++ _, errRsp = procLeaveEndpoint(c, vars, jlb) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ _, errRsp = procLeaveEndpoint(c, vars, jlb) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, got: %v", errRsp) +++ } +++ +++ _, errRsp = procDeleteEndpoint(c, vars, nil) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++} +++ +++func TestFindEndpointUtilPanic(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ defer checkPanic(t) +++ c, nw := createTestNetwork(t, "network") +++ nid := nw.ID() +++ findEndpoint(c, nid, "", byID, -1) +++} +++ +++func TestFindServiceUtilPanic(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ defer checkPanic(t) +++ c, _ := createTestNetwork(t, "network") +++ findService(c, "random_service", -1) +++} +++ +++func TestFindEndpointUtil(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, nw := createTestNetwork(t, "network") +++ nid := nw.ID() +++ +++ ep, err := nw.CreateEndpoint("secondEp", nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ eid := ep.ID() +++ +++ _, errRsp := findEndpoint(c, nid, "", byID, byName) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, but got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusBadRequest { +++ t.Fatalf("Expected %d, but got: %d", http.StatusBadRequest, errRsp.StatusCode) +++ } +++ +++ ep0, errRsp := findEndpoint(c, nid, "secondEp", byID, byName) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ ep1, errRsp := findEndpoint(c, "network", "secondEp", byName, byName) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ ep2, errRsp := findEndpoint(c, nid, eid, byID, byID) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ ep3, errRsp := findEndpoint(c, "network", eid, byName, byID) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ ep4, errRsp := findService(c, "secondEp", byName) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ ep5, errRsp := findService(c, eid, byID) +++ if errRsp != &successResponse { +++ t.Fatalf("Unexepected failure: %v", errRsp) +++ } +++ +++ if ep0 != ep1 || ep0 != ep2 || ep0 != ep3 || ep0 != ep4 || ep0 != ep5 { +++ t.Fatalf("Diffenrent queries returned different endpoints") +++ } +++ +++ ep.Delete() +++ +++ _, errRsp = findEndpoint(c, nid, "secondEp", byID, byName) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, but got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) +++ } +++ +++ _, errRsp = findEndpoint(c, "network", "secondEp", byName, byName) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, but got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) +++ } +++ +++ _, errRsp = findEndpoint(c, nid, eid, byID, byID) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, but got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) +++ } +++ +++ _, errRsp = findEndpoint(c, "network", eid, byName, byID) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, but got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) +++ } +++ +++ _, errRsp = findService(c, "secondEp", byName) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, but got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) +++ } +++ +++ _, errRsp = findService(c, eid, byID) +++ if errRsp == &successResponse { +++ t.Fatalf("Expected failure, but got: %v", errRsp) +++ } +++ if errRsp.StatusCode != http.StatusNotFound { +++ t.Fatalf("Expected %d, but got: %d", http.StatusNotFound, errRsp.StatusCode) +++ } +++} +++ +++func TestEndpointToService(t *testing.T) { +++ r := &responseStatus{Status: "this is one endpoint", StatusCode: http.StatusOK} +++ r = endpointToService(r) +++ if r.Status != "this is one service" { +++ t.Fatalf("endpointToService returned unexpected status string: %s", r.Status) +++ } +++ +++ r = &responseStatus{Status: "this is one network", StatusCode: http.StatusOK} +++ r = endpointToService(r) +++ if r.Status != "this is one network" { +++ t.Fatalf("endpointToService returned unexpected status string: %s", r.Status) +++ } +++} +++ +++func checkPanic(t *testing.T) { +++ if r := recover(); r != nil { +++ if _, ok := r.(runtime.Error); ok { +++ panic(r) +++ } +++ } else { +++ t.Fatalf("Expected to panic, but suceeded") +++ } +++} +++ +++func TestDetectNetworkTargetPanic(t *testing.T) { +++ defer checkPanic(t) +++ vars := make(map[string]string) +++ detectNetworkTarget(vars) +++} +++ +++func TestDetectEndpointTargetPanic(t *testing.T) { +++ defer checkPanic(t) +++ vars := make(map[string]string) +++ detectEndpointTarget(vars) +++} +++ +++func TestResponseStatus(t *testing.T) { +++ list := []int{ +++ http.StatusBadGateway, +++ http.StatusBadRequest, +++ http.StatusConflict, +++ http.StatusContinue, +++ http.StatusExpectationFailed, +++ http.StatusForbidden, +++ http.StatusFound, +++ http.StatusGatewayTimeout, +++ http.StatusGone, +++ http.StatusHTTPVersionNotSupported, +++ http.StatusInternalServerError, +++ http.StatusLengthRequired, +++ http.StatusMethodNotAllowed, +++ http.StatusMovedPermanently, +++ http.StatusMultipleChoices, +++ http.StatusNoContent, +++ http.StatusNonAuthoritativeInfo, +++ http.StatusNotAcceptable, +++ http.StatusNotFound, +++ http.StatusNotModified, +++ http.StatusPartialContent, +++ http.StatusPaymentRequired, +++ http.StatusPreconditionFailed, +++ http.StatusProxyAuthRequired, +++ http.StatusRequestEntityTooLarge, +++ http.StatusRequestTimeout, +++ http.StatusRequestURITooLong, +++ http.StatusRequestedRangeNotSatisfiable, +++ http.StatusResetContent, +++ http.StatusServiceUnavailable, +++ http.StatusSwitchingProtocols, +++ http.StatusTemporaryRedirect, +++ http.StatusUnauthorized, +++ http.StatusUnsupportedMediaType, +++ http.StatusUseProxy, +++ } +++ for _, c := range list { +++ r := responseStatus{StatusCode: c} +++ if r.isOK() { +++ t.Fatalf("isOK() returned true for code% d", c) +++ } +++ } +++ +++ r := responseStatus{StatusCode: http.StatusOK} +++ if !r.isOK() { +++ t.Fatalf("isOK() failed") +++ } +++ +++ r = responseStatus{StatusCode: http.StatusCreated} +++ if !r.isOK() { +++ t.Fatalf("isOK() failed") +++ } +++} +++ +++// Local structs for end to end testing of api.go +++type localReader struct { +++ data []byte +++ beBad bool +++} +++ +++func newLocalReader(data []byte) *localReader { +++ lr := &localReader{data: make([]byte, len(data))} +++ copy(lr.data, data) +++ return lr +++} +++ +++func (l *localReader) Read(p []byte) (n int, err error) { +++ if l.beBad { +++ return 0, errors.New("I am a bad reader") +++ } +++ if p == nil { +++ return -1, fmt.Errorf("nil buffer passed") +++ } +++ if l.data == nil || len(l.data) == 0 { +++ return 0, io.EOF +++ } +++ copy(p[:], l.data[:]) +++ return len(l.data), io.EOF +++} +++ +++type localResponseWriter struct { +++ body []byte +++ statusCode int +++} +++ +++func newWriter() *localResponseWriter { +++ return &localResponseWriter{} +++} +++ +++func (f *localResponseWriter) Header() http.Header { +++ return make(map[string][]string, 0) +++} +++ +++func (f *localResponseWriter) Write(data []byte) (int, error) { +++ if data == nil { +++ return -1, fmt.Errorf("nil data passed") +++ } +++ +++ f.body = make([]byte, len(data)) +++ copy(f.body, data) +++ +++ return len(f.body), nil +++} +++ +++func (f *localResponseWriter) WriteHeader(c int) { +++ f.statusCode = c +++} +++ +++func TestwriteJSON(t *testing.T) { +++ testCode := 55 +++ testData, err := json.Marshal("test data") +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ rsp := newWriter() +++ writeJSON(rsp, testCode, testData) +++ if rsp.statusCode != testCode { +++ t.Fatalf("writeJSON() failed to set the status code. Expected %d. Got %d", testCode, rsp.statusCode) +++ } +++ if !bytes.Equal(testData, rsp.body) { +++ t.Fatalf("writeJSON() failed to set the body. Expected %s. Got %s", testData, rsp.body) +++ } +++ +++} +++ +++func TestHttpHandlerUninit(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ c, err := libnetwork.New() +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ h := &httpHandler{c: c} +++ h.initRouter() +++ if h.r == nil { +++ t.Fatalf("initRouter() did not initialize the router") +++ } +++ +++ rsp := newWriter() +++ req, err := http.NewRequest("GET", "/v1.19/networks", nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ handleRequest := NewHTTPHandler(nil) +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusServiceUnavailable { +++ t.Fatalf("Expected (%d). Got (%d): %s", http.StatusServiceUnavailable, rsp.statusCode, rsp.body) +++ } +++ +++ handleRequest = NewHTTPHandler(c) +++ +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Expected (%d). Got: (%d): %s", http.StatusOK, rsp.statusCode, rsp.body) +++ } +++ +++ var list []*networkResource +++ err = json.Unmarshal(rsp.body, &list) +++ if err != nil { +++ t.Fatal(err) +++ } +++ if len(list) != 0 { +++ t.Fatalf("Expected empty list. Got %v", list) +++ } +++ +++ n, err := c.NewNetwork(bridgeNetType, "didietro", nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ nwr := buildNetworkResource(n) +++ expected, err := json.Marshal([]*networkResource{nwr}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) +++ } +++ if len(rsp.body) == 0 { +++ t.Fatalf("Empty list of networks") +++ } +++ if bytes.Equal(rsp.body, expected) { +++ t.Fatalf("Incorrect list of networks in response's body") +++ } +++} +++ +++func TestHttpHandlerBadBody(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ rsp := newWriter() +++ +++ c, err := libnetwork.New() +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest := NewHTTPHandler(c) +++ +++ req, err := http.NewRequest("POST", "/v1.19/networks", &localReader{beBad: true}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusBadRequest { +++ t.Fatalf("Unexpected status code. Expected (%d). Got (%d): %s.", http.StatusBadRequest, rsp.statusCode, string(rsp.body)) +++ } +++ +++ body := []byte{} +++ lr := newLocalReader(body) +++ req, err = http.NewRequest("POST", "/v1.19/networks", lr) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusBadRequest { +++ t.Fatalf("Unexpected status code. Expected (%d). Got (%d): %s.", http.StatusBadRequest, rsp.statusCode, string(rsp.body)) +++ } +++} +++ +++func TestEndToEnd(t *testing.T) { +++ defer sandbox.SetupTestOSContext(t)() +++ +++ rsp := newWriter() +++ +++ c, err := libnetwork.New() +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest := NewHTTPHandler(c) +++ +++ ops := options.Generic{ +++ netlabel.EnableIPv6: true, +++ netlabel.GenericData: map[string]string{ +++ "BridgeName": "cdef", +++ "FixedCIDRv6": "fe80:2000::1/64", +++ "EnableIPv6": "true", +++ "Mtu": "1460", +++ "EnableIPTables": "true", +++ "AddressIP": "172.28.30.254/16", +++ "EnableUserlandProxy": "true", +++ "AllowNonDefaultBridge": "true", +++ }, +++ } +++ +++ // Create network +++ nc := networkCreate{Name: "network-fiftyfive", NetworkType: bridgeNetType, Options: ops} +++ body, err := json.Marshal(nc) +++ if err != nil { +++ t.Fatal(err) +++ } +++ lr := newLocalReader(body) +++ req, err := http.NewRequest("POST", "/v1.19/networks", lr) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusCreated { +++ t.Fatalf("Unexpectded status code. Expected (%d). Got (%d): %s.", http.StatusCreated, rsp.statusCode, string(rsp.body)) +++ } +++ if len(rsp.body) == 0 { +++ t.Fatalf("Empty response body") +++ } +++ +++ var nid string +++ err = json.Unmarshal(rsp.body, &nid) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ // Query networks collection +++ req, err = http.NewRequest("GET", "/v1.19/networks", nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body) +++ } +++ +++ b0 := make([]byte, len(rsp.body)) +++ copy(b0, rsp.body) +++ +++ req, err = http.NewRequest("GET", "/v1.19/networks?name=network-fiftyfive", nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body) +++ } +++ +++ if !bytes.Equal(b0, rsp.body) { +++ t.Fatalf("Expected same body from GET /networks and GET /networks?name= when only network exist.") +++ } +++ +++ // Query network by name +++ req, err = http.NewRequest("GET", "/v1.19/networks?name=culo", nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body) +++ } +++ +++ var list []*networkResource +++ err = json.Unmarshal(rsp.body, &list) +++ if err != nil { +++ t.Fatal(err) +++ } +++ if len(list) != 0 { +++ t.Fatalf("Expected empty list. Got %v", list) +++ } +++ +++ req, err = http.NewRequest("GET", "/v1.19/networks?name=network-fiftyfive", nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) +++ } +++ +++ err = json.Unmarshal(rsp.body, &list) +++ if err != nil { +++ t.Fatal(err) +++ } +++ if len(list) == 0 { +++ t.Fatalf("Expected non empty list") +++ } +++ if list[0].Name != "network-fiftyfive" || nid != list[0].ID { +++ t.Fatalf("Incongruent resource found: %v", list[0]) +++ } +++ +++ // Query network by partial id +++ chars := []byte(nid) +++ partial := string(chars[0 : len(chars)/2]) +++ req, err = http.NewRequest("GET", "/v1.19/networks?partial-id="+partial, nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) +++ } +++ +++ err = json.Unmarshal(rsp.body, &list) +++ if err != nil { +++ t.Fatal(err) +++ } +++ if len(list) == 0 { +++ t.Fatalf("Expected non empty list") +++ } +++ if list[0].Name != "network-fiftyfive" || nid != list[0].ID { +++ t.Fatalf("Incongruent resource found: %v", list[0]) +++ } +++ +++ // Get network by id +++ req, err = http.NewRequest("GET", "/v1.19/networks/"+nid, nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) +++ } +++ +++ var nwr networkResource +++ err = json.Unmarshal(rsp.body, &nwr) +++ if err != nil { +++ t.Fatal(err) +++ } +++ if nwr.Name != "network-fiftyfive" || nid != nwr.ID { +++ t.Fatalf("Incongruent resource found: %v", nwr) +++ } +++ +++ // Create endpoint +++ eb, err := json.Marshal(endpointCreate{Name: "ep-TwentyTwo"}) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ lr = newLocalReader(eb) +++ req, err = http.NewRequest("POST", "/v1.19/networks/"+nid+"/endpoints", lr) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusCreated { +++ t.Fatalf("Unexpectded status code. Expected (%d). Got (%d): %s.", http.StatusCreated, rsp.statusCode, string(rsp.body)) +++ } +++ if len(rsp.body) == 0 { +++ t.Fatalf("Empty response body") +++ } +++ +++ var eid string +++ err = json.Unmarshal(rsp.body, &eid) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ // Query endpoint(s) +++ req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints", nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body) +++ } +++ +++ req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints?name=bla", nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) +++ } +++ var epList []*endpointResource +++ err = json.Unmarshal(rsp.body, &epList) +++ if err != nil { +++ t.Fatal(err) +++ } +++ if len(epList) != 0 { +++ t.Fatalf("Expected empty list. Got %v", epList) +++ } +++ +++ // Query endpoint by name +++ req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints?name=ep-TwentyTwo", nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) +++ } +++ +++ err = json.Unmarshal(rsp.body, &epList) +++ if err != nil { +++ t.Fatal(err) +++ } +++ if len(epList) == 0 { +++ t.Fatalf("Empty response body") +++ } +++ if epList[0].Name != "ep-TwentyTwo" || eid != epList[0].ID { +++ t.Fatalf("Incongruent resource found: %v", epList[0]) +++ } +++ +++ // Query endpoint by partial id +++ chars = []byte(eid) +++ partial = string(chars[0 : len(chars)/2]) +++ req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints?partial-id="+partial, nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) +++ } +++ +++ err = json.Unmarshal(rsp.body, &epList) +++ if err != nil { +++ t.Fatal(err) +++ } +++ if len(epList) == 0 { +++ t.Fatalf("Empty response body") +++ } +++ if epList[0].Name != "ep-TwentyTwo" || eid != epList[0].ID { +++ t.Fatalf("Incongruent resource found: %v", epList[0]) +++ } +++ +++ // Get endpoint by id +++ req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints/"+eid, nil) +++ if err != nil { +++ t.Fatal(err) +++ } +++ handleRequest(rsp, req) +++ if rsp.statusCode != http.StatusOK { +++ t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) +++ } +++ +++ var epr endpointResource +++ err = json.Unmarshal(rsp.body, &epr) +++ if err != nil { +++ t.Fatal(err) +++ } +++ if epr.Name != "ep-TwentyTwo" || epr.ID != eid { +++ t.Fatalf("Incongruent resource found: %v", epr) +++ } +++} +++ +++type bre struct{} +++ +++func (b *bre) Error() string { +++ return "I am a bad request error" +++} +++func (b *bre) BadRequest() {} +++ +++type nfe struct{} +++ +++func (n *nfe) Error() string { +++ return "I am a not found error" +++} +++func (n *nfe) NotFound() {} +++ +++type forb struct{} +++ +++func (f *forb) Error() string { +++ return "I am a bad request error" +++} +++func (f *forb) Forbidden() {} +++ +++type notimpl struct{} +++ +++func (nip *notimpl) Error() string { +++ return "I am a not implemented error" +++} +++func (nip *notimpl) NotImplemented() {} +++ +++type inter struct{} +++ +++func (it *inter) Error() string { +++ return "I am a internal error" +++} +++func (it *inter) Internal() {} +++ +++type tout struct{} +++ +++func (to *tout) Error() string { +++ return "I am a timeout error" +++} +++func (to *tout) Timeout() {} +++ +++type noserv struct{} +++ +++func (nos *noserv) Error() string { +++ return "I am a no service error" +++} +++func (nos *noserv) NoService() {} +++ +++type notclassified struct{} +++ +++func (noc *notclassified) Error() string { +++ return "I am a non classified error" +++} +++ +++func TestErrorConversion(t *testing.T) { +++ if convertNetworkError(new(bre)).StatusCode != http.StatusBadRequest { +++ t.Fatalf("Failed to recognize BadRequest error") +++ } +++ +++ if convertNetworkError(new(nfe)).StatusCode != http.StatusNotFound { +++ t.Fatalf("Failed to recognize NotFound error") +++ } +++ +++ if convertNetworkError(new(forb)).StatusCode != http.StatusForbidden { +++ t.Fatalf("Failed to recognize Forbidden error") +++ } +++ +++ if convertNetworkError(new(notimpl)).StatusCode != http.StatusNotImplemented { +++ t.Fatalf("Failed to recognize NotImplemented error") +++ } +++ +++ if convertNetworkError(new(inter)).StatusCode != http.StatusInternalServerError { +++ t.Fatalf("Failed to recognize Internal error") +++ } +++ +++ if convertNetworkError(new(tout)).StatusCode != http.StatusRequestTimeout { +++ t.Fatalf("Failed to recognize Timeout error") +++ } +++ +++ if convertNetworkError(new(noserv)).StatusCode != http.StatusServiceUnavailable { +++ t.Fatalf("Failed to recognize No Service error") +++ } +++ +++ if convertNetworkError(new(notclassified)).StatusCode != http.StatusInternalServerError { +++ t.Fatalf("Failed to recognize not classified error as Internal error") +++ } +++} diff --cc libnetwork/api/types.go index 00000000,00000000,00000000..72f20db2 new file mode 100644 --- /dev/null +++ b/libnetwork/api/types.go @@@@ -1,0 -1,0 -1,0 +1,81 @@@@ +++package api +++ +++import "github.com/docker/libnetwork/types" +++ +++/*********** +++ Resources +++************/ +++ +++// networkResource is the body of the "get network" http response message +++type networkResource struct { +++ Name string `json:"name"` +++ ID string `json:"id"` +++ Type string `json:"type"` +++ Endpoints []*endpointResource `json:"endpoints"` +++} +++ +++// endpointResource is the body of the "get endpoint" http response message +++type endpointResource struct { +++ Name string `json:"name"` +++ ID string `json:"id"` +++ Network string `json:"network"` +++} +++ +++// containerResource is the body of "get service backend" response message +++type containerResource struct { +++ ID string `json:"id"` +++ // will add more fields once labels change is in +++} +++ +++/*********** +++ Body types +++ ************/ +++ +++// networkCreate is the expected body of the "create network" http request message +++type networkCreate struct { +++ Name string `json:"name"` +++ NetworkType string `json:"network_type"` +++ Options map[string]interface{} `json:"options"` +++} +++ +++// endpointCreate represents the body of the "create endpoint" http request message +++type endpointCreate struct { +++ Name string `json:"name"` +++ ExposedPorts []types.TransportPort `json:"exposed_ports"` +++ PortMapping []types.PortBinding `json:"port_mapping"` +++} +++ +++// endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages +++type endpointJoin struct { +++ ContainerID string `json:"container_id"` +++ HostName string `json:"host_name"` +++ DomainName string `json:"domain_name"` +++ HostsPath string `json:"hosts_path"` +++ ResolvConfPath string `json:"resolv_conf_path"` +++ DNS []string `json:"dns"` +++ ExtraHosts []endpointExtraHost `json:"extra_hosts"` +++ ParentUpdates []endpointParentUpdate `json:"parent_updates"` +++ UseDefaultSandbox bool `json:"use_default_sandbox"` +++} +++ +++// servicePublish represents the body of the "publish service" http request message +++type servicePublish struct { +++ Name string `json:"name"` +++ Network string `json:"network_name"` +++ ExposedPorts []types.TransportPort `json:"exposed_ports"` +++ PortMapping []types.PortBinding `json:"port_mapping"` +++} +++ +++// EndpointExtraHost represents the extra host object +++type endpointExtraHost struct { +++ Name string `json:"name"` +++ Address string `json:"address"` +++} +++ +++// EndpointParentUpdate is the object carrying the information about the +++// endpoint parent that needs to be updated +++type endpointParentUpdate struct { +++ EndpointID string `json:"endpoint_id"` +++ Name string `json:"name"` +++ Address string `json:"address"` +++} diff --cc libnetwork/bitseq/sequence.go index 00000000,00000000,00000000..d9c9ad52 new file mode 100644 --- /dev/null +++ b/libnetwork/bitseq/sequence.go @@@@ -1,0 -1,0 -1,0 +1,454 @@@@ +++// Package bitseq provides a structure and utilities for representing long bitmask +++// as sequence of run-lenght encoded blocks. It operates direclty on the encoded +++// representation, it does not decode/encode. +++package bitseq +++ +++import ( +++ "fmt" +++ "sync" +++ +++ "github.com/docker/libnetwork/datastore" +++ "github.com/docker/libnetwork/netutils" +++) +++ +++// Block Sequence constants +++// If needed we can think of making these configurable +++const ( +++ blockLen = 32 +++ blockBytes = blockLen / 8 +++ blockMAX = 1<%s", s.Block, s.Count, nextBlock) +++} +++ +++// GetAvailableBit returns the position of the first unset bit in the bitmask represented by this sequence +++func (s *Sequence) GetAvailableBit() (bytePos, bitPos int) { +++ if s.Block == blockMAX || s.Count == 0 { +++ return -1, -1 +++ } +++ bits := 0 +++ bitSel := uint32(blockFirstBit) +++ for bitSel > 0 && s.Block&bitSel != 0 { +++ bitSel >>= 1 +++ bits++ +++ } +++ return bits / 8, bits % 8 +++} +++ +++// GetCopy returns a copy of the linked list rooted at this node +++func (s *Sequence) GetCopy() *Sequence { +++ n := &Sequence{Block: s.Block, Count: s.Count} +++ pn := n +++ ps := s.Next +++ for ps != nil { +++ pn.Next = &Sequence{Block: ps.Block, Count: ps.Count} +++ pn = pn.Next +++ ps = ps.Next +++ } +++ return n +++} +++ +++// Equal checks if this sequence is equal to the passed one +++func (s *Sequence) Equal(o *Sequence) bool { +++ this := s +++ other := o +++ for this != nil { +++ if other == nil { +++ return false +++ } +++ if this.Block != other.Block || this.Count != other.Count { +++ return false +++ } +++ this = this.Next +++ other = other.Next +++ } +++ // Check if other is longer than this +++ if other != nil { +++ return false +++ } +++ return true +++} +++ +++// ToByteArray converts the sequence into a byte array +++// TODO (aboch): manage network/host order stuff +++func (s *Sequence) ToByteArray() ([]byte, error) { +++ var bb []byte +++ +++ p := s +++ for p != nil { +++ bb = append(bb, netutils.U32ToA(p.Block)...) +++ bb = append(bb, netutils.U32ToA(p.Count)...) +++ p = p.Next +++ } +++ +++ return bb, nil +++} +++ +++// FromByteArray construct the sequence from the byte array +++// TODO (aboch): manage network/host order stuff +++func (s *Sequence) FromByteArray(data []byte) error { +++ l := len(data) +++ if l%8 != 0 { +++ return fmt.Errorf("cannot deserialize byte sequence of lenght %d (%v)", l, data) +++ } +++ +++ p := s +++ i := 0 +++ for { +++ p.Block = netutils.ATo32(data[i : i+4]) +++ p.Count = netutils.ATo32(data[i+4 : i+8]) +++ i += 8 +++ if i == l { +++ break +++ } +++ p.Next = &Sequence{} +++ p = p.Next +++ } +++ +++ return nil +++} +++ +++// GetFirstAvailable returns the byte and bit position of the first unset bit +++func (h *Handle) GetFirstAvailable() (int, int, error) { +++ h.Lock() +++ defer h.Unlock() +++ return GetFirstAvailable(h.head) +++} +++ +++// CheckIfAvailable checks if the bit correspondent to the specified ordinal is unset +++// If the ordinal is beyond the Sequence limits, a negative response is returned +++func (h *Handle) CheckIfAvailable(ordinal int) (int, int, error) { +++ h.Lock() +++ defer h.Unlock() +++ return CheckIfAvailable(h.head, ordinal) +++} +++ +++// PushReservation pushes the bit reservation inside the bitmask. +++func (h *Handle) PushReservation(bytePos, bitPos int, release bool) error { +++ // Create a copy of the current handler +++ h.Lock() +++ nh := &Handle{ +++ app: h.app, +++ id: h.id, +++ store: h.store, +++ dbIndex: h.dbIndex, +++ head: h.head.GetCopy(), +++ dbExists: h.dbExists, +++ } +++ h.Unlock() +++ +++ nh.head = PushReservation(bytePos, bitPos, nh.head, release) +++ +++ err := nh.writeToStore() +++ if err == nil { +++ // Commit went through, save locally +++ h.Lock() +++ h.head = nh.head +++ if release { +++ h.unselected++ +++ } else { +++ h.unselected-- +++ } +++ // Can't use SetIndex() since we're locked. +++ h.dbIndex = nh.Index() +++ h.dbExists = true +++ h.Unlock() +++ } +++ +++ return err +++} +++ +++// Destroy removes from the datastore the data belonging to this handle +++func (h *Handle) Destroy() { +++ h.deleteFromStore() +++} +++ +++// ToByteArray converts this handle's data into a byte array +++func (h *Handle) ToByteArray() ([]byte, error) { +++ ba := make([]byte, 8) +++ +++ h.Lock() +++ defer h.Unlock() +++ copy(ba[0:4], netutils.U32ToA(h.bits)) +++ copy(ba[4:8], netutils.U32ToA(h.unselected)) +++ bm, err := h.head.ToByteArray() +++ if err != nil { +++ return nil, fmt.Errorf("failed to serialize head: %s", err.Error()) +++ } +++ ba = append(ba, bm...) +++ +++ return ba, nil +++} +++ +++// FromByteArray reads his handle's data from a byte array +++func (h *Handle) FromByteArray(ba []byte) error { +++ if ba == nil { +++ return fmt.Errorf("nil byte array") +++ } +++ +++ nh := &Sequence{} +++ err := nh.FromByteArray(ba[8:]) +++ if err != nil { +++ return fmt.Errorf("failed to deserialize head: %s", err.Error()) +++ } +++ +++ h.Lock() +++ h.head = nh +++ h.bits = netutils.ATo32(ba[0:4]) +++ h.unselected = netutils.ATo32(ba[4:8]) +++ h.Unlock() +++ +++ return nil +++} +++ +++// Bits returns the length of the bit sequence +++func (h *Handle) Bits() uint32 { +++ return h.bits +++} +++ +++// Unselected returns the number of bits which are not selected +++func (h *Handle) Unselected() uint32 { +++ h.Lock() +++ defer h.Unlock() +++ return h.unselected +++} +++ +++// GetFirstAvailable looks for the first unset bit in passed mask +++func GetFirstAvailable(head *Sequence) (int, int, error) { +++ byteIndex := 0 +++ current := head +++ for current != nil { +++ if current.Block != blockMAX { +++ bytePos, bitPos := current.GetAvailableBit() +++ return byteIndex + bytePos, bitPos, nil +++ } +++ byteIndex += int(current.Count * blockBytes) +++ current = current.Next +++ } +++ return -1, -1, fmt.Errorf("no bit available") +++} +++ +++// CheckIfAvailable checks if the bit correspondent to the specified ordinal is unset +++// If the ordinal is beyond the Sequence limits, a negative response is returned +++func CheckIfAvailable(head *Sequence, ordinal int) (int, int, error) { +++ bytePos := ordinal / 8 +++ bitPos := ordinal % 8 +++ +++ // Find the Sequence containing this byte +++ current, _, _, inBlockBytePos := findSequence(head, bytePos) +++ +++ if current != nil { +++ // Check whether the bit corresponding to the ordinal address is unset +++ bitSel := uint32(blockFirstBit >> uint(inBlockBytePos*8+bitPos)) +++ if current.Block&bitSel == 0 { +++ return bytePos, bitPos, nil +++ } +++ } +++ +++ return -1, -1, fmt.Errorf("requested bit is not available") +++} +++ +++// Given the byte position and the sequences list head, return the pointer to the +++// sequence containing the byte (current), the pointer to the previous sequence, +++// the number of blocks preceding the block containing the byte inside the current sequence. +++// If bytePos is outside of the list, function will return (nil, nil, 0, -1) +++func findSequence(head *Sequence, bytePos int) (*Sequence, *Sequence, uint32, int) { +++ // Find the Sequence containing this byte +++ previous := head +++ current := head +++ n := bytePos +++ for current.Next != nil && n >= int(current.Count*blockBytes) { // Nil check for less than 32 addresses masks +++ n -= int(current.Count * blockBytes) +++ previous = current +++ current = current.Next +++ } +++ +++ // If byte is outside of the list, let caller know +++ if n >= int(current.Count*blockBytes) { +++ return nil, nil, 0, -1 +++ } +++ +++ // Find the byte position inside the block and the number of blocks +++ // preceding the block containing the byte inside this sequence +++ precBlocks := uint32(n / blockBytes) +++ inBlockBytePos := bytePos % blockBytes +++ +++ return current, previous, precBlocks, inBlockBytePos +++} +++ +++// PushReservation pushes the bit reservation inside the bitmask. +++// Given byte and bit positions, identify the sequence (current) which holds the block containing the affected bit. +++// Create a new block with the modified bit according to the operation (allocate/release). +++// Create a new Sequence containing the new Block and insert it in the proper position. +++// Remove current sequence if empty. +++// Check if new Sequence can be merged with neighbour (previous/Next) sequences. +++// +++// +++// Identify "current" Sequence containing block: +++// [prev seq] [current seq] [Next seq] +++// +++// Based on block position, resulting list of sequences can be any of three forms: +++// +++// Block position Resulting list of sequences +++// A) Block is first in current: [prev seq] [new] [modified current seq] [Next seq] +++// B) Block is last in current: [prev seq] [modified current seq] [new] [Next seq] +++// C) Block is in the middle of current: [prev seq] [curr pre] [new] [curr post] [Next seq] +++func PushReservation(bytePos, bitPos int, head *Sequence, release bool) *Sequence { +++ // Store list's head +++ newHead := head +++ +++ // Find the Sequence containing this byte +++ current, previous, precBlocks, inBlockBytePos := findSequence(head, bytePos) +++ if current == nil { +++ return newHead +++ } +++ +++ // Construct updated block +++ bitSel := uint32(blockFirstBit >> uint(inBlockBytePos*8+bitPos)) +++ newBlock := current.Block +++ if release { +++ newBlock &^= bitSel +++ } else { +++ newBlock |= bitSel +++ } +++ +++ // Quit if it was a redundant request +++ if current.Block == newBlock { +++ return newHead +++ } +++ +++ // Current Sequence inevitably looses one block, upadate Count +++ current.Count-- +++ +++ // Create new sequence +++ newSequence := &Sequence{Block: newBlock, Count: 1} +++ +++ // Insert the new sequence in the list based on block position +++ if precBlocks == 0 { // First in sequence (A) +++ newSequence.Next = current +++ if current == head { +++ newHead = newSequence +++ previous = newHead +++ } else { +++ previous.Next = newSequence +++ } +++ removeCurrentIfEmpty(&newHead, newSequence, current) +++ mergeSequences(previous) +++ } else if precBlocks == current.Count-2 { // Last in sequence (B) +++ newSequence.Next = current.Next +++ current.Next = newSequence +++ mergeSequences(current) +++ } else { // In between the sequence (C) +++ currPre := &Sequence{Block: current.Block, Count: precBlocks, Next: newSequence} +++ currPost := current +++ currPost.Count -= precBlocks +++ newSequence.Next = currPost +++ if currPost == head { +++ newHead = currPre +++ } else { +++ previous.Next = currPre +++ } +++ // No merging or empty current possible here +++ } +++ +++ return newHead +++} +++ +++// Removes the current sequence from the list if empty, adjusting the head pointer if needed +++func removeCurrentIfEmpty(head **Sequence, previous, current *Sequence) { +++ if current.Count == 0 { +++ if current == *head { +++ *head = current.Next +++ } else { +++ previous.Next = current.Next +++ current = current.Next +++ } +++ } +++} +++ +++// Given a pointer to a Sequence, it checks if it can be merged with any following sequences +++// It stops when no more merging is possible. +++// TODO: Optimization: only attempt merge from start to end sequence, no need to scan till the end of the list +++func mergeSequences(seq *Sequence) { +++ if seq != nil { +++ // Merge all what possible from seq +++ for seq.Next != nil && seq.Block == seq.Next.Block { +++ seq.Count += seq.Next.Count +++ seq.Next = seq.Next.Next +++ } +++ // Move to Next +++ mergeSequences(seq.Next) +++ } +++} +++ +++func getNumBlocks(numBits uint32) uint32 { +++ numBlocks := numBits / blockLen +++ if numBits%blockLen != 0 { +++ numBlocks++ +++ } +++ return numBlocks +++} diff --cc libnetwork/bitseq/sequence_test.go index 00000000,00000000,00000000..54e505f7 new file mode 100644 --- /dev/null +++ b/libnetwork/bitseq/sequence_test.go @@@@ -1,0 -1,0 -1,0 +1,427 @@@@ +++package bitseq +++ +++import ( +++ "testing" +++) +++ +++func TestSequenceGetAvailableBit(t *testing.T) { +++ input := []struct { +++ head *Sequence +++ bytePos int +++ bitPos int +++ }{ +++ {&Sequence{Block: 0x0, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0x0, Count: 1}, 0, 0}, +++ {&Sequence{Block: 0x0, Count: 100}, 0, 0}, +++ +++ {&Sequence{Block: 0x80000000, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0x80000000, Count: 1}, 0, 1}, +++ {&Sequence{Block: 0x80000000, Count: 100}, 0, 1}, +++ +++ {&Sequence{Block: 0xFF000000, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0xFF000000, Count: 1}, 1, 0}, +++ {&Sequence{Block: 0xFF000000, Count: 100}, 1, 0}, +++ +++ {&Sequence{Block: 0xFF800000, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0xFF800000, Count: 1}, 1, 1}, +++ {&Sequence{Block: 0xFF800000, Count: 100}, 1, 1}, +++ +++ {&Sequence{Block: 0xFFC0FF00, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0xFFC0FF00, Count: 1}, 1, 2}, +++ {&Sequence{Block: 0xFFC0FF00, Count: 100}, 1, 2}, +++ +++ {&Sequence{Block: 0xFFE0FF00, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0xFFE0FF00, Count: 1}, 1, 3}, +++ {&Sequence{Block: 0xFFE0FF00, Count: 100}, 1, 3}, +++ +++ {&Sequence{Block: 0xFFFEFF00, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0xFFFEFF00, Count: 1}, 1, 7}, +++ {&Sequence{Block: 0xFFFEFF00, Count: 100}, 1, 7}, +++ +++ {&Sequence{Block: 0xFFFFC0FF, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0xFFFFC0FF, Count: 1}, 2, 2}, +++ {&Sequence{Block: 0xFFFFC0FF, Count: 100}, 2, 2}, +++ +++ {&Sequence{Block: 0xFFFFFF00, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0xFFFFFF00, Count: 1}, 3, 0}, +++ {&Sequence{Block: 0xFFFFFF00, Count: 100}, 3, 0}, +++ +++ {&Sequence{Block: 0xFFFFFFFE, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0xFFFFFFFE, Count: 1}, 3, 7}, +++ {&Sequence{Block: 0xFFFFFFFE, Count: 100}, 3, 7}, +++ +++ {&Sequence{Block: 0xFFFFFFFF, Count: 0}, -1, -1}, +++ {&Sequence{Block: 0xFFFFFFFF, Count: 1}, -1, -1}, +++ {&Sequence{Block: 0xFFFFFFFF, Count: 100}, -1, -1}, +++ } +++ +++ for n, i := range input { +++ b, bb := i.head.GetAvailableBit() +++ if b != i.bytePos || bb != i.bitPos { +++ t.Fatalf("Error in Sequence.getAvailableBit() (%d).\nExp: (%d, %d)\nGot: (%d, %d),", n, i.bytePos, i.bitPos, b, bb) +++ } +++ } +++} +++ +++func TestSequenceEqual(t *testing.T) { +++ input := []struct { +++ first *Sequence +++ second *Sequence +++ areEqual bool +++ }{ +++ {&Sequence{Block: 0x0, Count: 8, Next: nil}, &Sequence{Block: 0x0, Count: 8}, true}, +++ {&Sequence{Block: 0x0, Count: 0, Next: nil}, &Sequence{Block: 0x0, Count: 0}, true}, +++ {&Sequence{Block: 0x0, Count: 2, Next: nil}, &Sequence{Block: 0x0, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}, false}, +++ {&Sequence{Block: 0x0, Count: 2, Next: &Sequence{Block: 0x1, Count: 1}}, &Sequence{Block: 0x0, Count: 2}, false}, +++ +++ {&Sequence{Block: 0x12345678, Count: 8, Next: nil}, &Sequence{Block: 0x12345678, Count: 8}, true}, +++ {&Sequence{Block: 0x12345678, Count: 8, Next: nil}, &Sequence{Block: 0x12345678, Count: 9}, false}, +++ {&Sequence{Block: 0x12345678, Count: 1, Next: &Sequence{Block: 0XFFFFFFFF, Count: 1}}, &Sequence{Block: 0x12345678, Count: 1}, false}, +++ {&Sequence{Block: 0x12345678, Count: 1}, &Sequence{Block: 0x12345678, Count: 1, Next: &Sequence{Block: 0XFFFFFFFF, Count: 1}}, false}, +++ } +++ +++ for n, i := range input { +++ if i.areEqual != i.first.Equal(i.second) { +++ t.Fatalf("Error in Sequence.Equal() (%d).\nExp: %t\nGot: %t,", n, i.areEqual, !i.areEqual) +++ } +++ } +++} +++ +++func TestSequenceCopy(t *testing.T) { +++ s := &Sequence{ +++ Block: 0x0, +++ Count: 8, +++ Next: &Sequence{ +++ Block: 0x0, +++ Count: 8, +++ Next: &Sequence{ +++ Block: 0x0, +++ Count: 0, +++ Next: &Sequence{ +++ Block: 0x0, +++ Count: 0, +++ Next: &Sequence{ +++ Block: 0x0, +++ Count: 2, +++ Next: &Sequence{ +++ Block: 0x0, +++ Count: 1, +++ Next: &Sequence{ +++ Block: 0x0, +++ Count: 1, +++ Next: &Sequence{ +++ Block: 0x0, +++ Count: 2, +++ Next: &Sequence{ +++ Block: 0x1, +++ Count: 1, +++ Next: &Sequence{ +++ Block: 0x0, +++ Count: 2, +++ Next: nil, +++ }, +++ }, +++ }, +++ }, +++ }, +++ }, +++ }, +++ }, +++ }, +++ } +++ +++ n := s.GetCopy() +++ if !s.Equal(n) { +++ t.Fatalf("copy of s failed") +++ } +++ if n == s { +++ t.Fatalf("not true copy of s") +++ } +++} +++ +++func TestGetFirstAvailable(t *testing.T) { +++ input := []struct { +++ mask *Sequence +++ bytePos int +++ bitPos int +++ }{ +++ {&Sequence{Block: 0xffffffff, Count: 2048}, -1, -1}, +++ {&Sequence{Block: 0x0, Count: 8}, 0, 0}, +++ {&Sequence{Block: 0x80000000, Count: 8}, 0, 1}, +++ {&Sequence{Block: 0xC0000000, Count: 8}, 0, 2}, +++ {&Sequence{Block: 0xE0000000, Count: 8}, 0, 3}, +++ {&Sequence{Block: 0xF0000000, Count: 8}, 0, 4}, +++ {&Sequence{Block: 0xF8000000, Count: 8}, 0, 5}, +++ {&Sequence{Block: 0xFC000000, Count: 8}, 0, 6}, +++ {&Sequence{Block: 0xFE000000, Count: 8}, 0, 7}, +++ +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x00000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 0}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 1}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 2}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xE0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 3}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 4}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF8000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 5}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFC000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 6}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFE000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 7}, +++ +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 0}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF800000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 1}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFC00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 2}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFE00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 3}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 4}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF80000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 5}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFC0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 6}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFE0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 7}, +++ +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 7, 7}, +++ +++ {&Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0x0, Count: 6}}, 8, 0}, +++ } +++ +++ for n, i := range input { +++ bytePos, bitPos, _ := GetFirstAvailable(i.mask) +++ if bytePos != i.bytePos || bitPos != i.bitPos { +++ t.Fatalf("Error in (%d) getFirstAvailable(). Expected (%d, %d). Got (%d, %d)", n, i.bytePos, i.bitPos, bytePos, bitPos) +++ } +++ } +++} +++ +++func TestFindSequence(t *testing.T) { +++ input := []struct { +++ head *Sequence +++ bytePos int +++ precBlocks uint32 +++ inBlockBytePos int +++ }{ +++ {&Sequence{Block: 0xffffffff, Count: 0}, 0, 0, -1}, +++ {&Sequence{Block: 0xffffffff, Count: 0}, 31, 0, -1}, +++ {&Sequence{Block: 0xffffffff, Count: 0}, 100, 0, -1}, +++ +++ {&Sequence{Block: 0x0, Count: 1}, 0, 0, 0}, +++ {&Sequence{Block: 0x0, Count: 1}, 1, 0, 1}, +++ {&Sequence{Block: 0x0, Count: 1}, 31, 0, -1}, +++ {&Sequence{Block: 0x0, Count: 1}, 60, 0, -1}, +++ +++ {&Sequence{Block: 0xffffffff, Count: 10}, 0, 0, 0}, +++ {&Sequence{Block: 0xffffffff, Count: 10}, 3, 0, 3}, +++ {&Sequence{Block: 0xffffffff, Count: 10}, 4, 1, 0}, +++ {&Sequence{Block: 0xffffffff, Count: 10}, 7, 1, 3}, +++ {&Sequence{Block: 0xffffffff, Count: 10}, 8, 2, 0}, +++ {&Sequence{Block: 0xffffffff, Count: 10}, 39, 9, 3}, +++ +++ {&Sequence{Block: 0xffffffff, Count: 10, Next: &Sequence{Block: 0xcc000000, Count: 10}}, 79, 9, 3}, +++ {&Sequence{Block: 0xffffffff, Count: 10, Next: &Sequence{Block: 0xcc000000, Count: 10}}, 80, 0, -1}, +++ } +++ +++ for n, i := range input { +++ _, _, precBlocks, inBlockBytePos := findSequence(i.head, i.bytePos) +++ if precBlocks != i.precBlocks || inBlockBytePos != i.inBlockBytePos { +++ t.Fatalf("Error in (%d) findSequence(). Expected (%d, %d). Got (%d, %d)", n, i.precBlocks, i.inBlockBytePos, precBlocks, inBlockBytePos) +++ } +++ } +++} +++ +++func TestCheckIfAvailable(t *testing.T) { +++ input := []struct { +++ head *Sequence +++ ordinal int +++ bytePos int +++ bitPos int +++ }{ +++ {&Sequence{Block: 0xffffffff, Count: 0}, 0, -1, -1}, +++ {&Sequence{Block: 0xffffffff, Count: 0}, 31, -1, -1}, +++ {&Sequence{Block: 0xffffffff, Count: 0}, 100, -1, -1}, +++ +++ {&Sequence{Block: 0x0, Count: 1}, 0, 0, 0}, +++ {&Sequence{Block: 0x0, Count: 1}, 1, 0, 1}, +++ {&Sequence{Block: 0x0, Count: 1}, 31, 3, 7}, +++ {&Sequence{Block: 0x0, Count: 1}, 60, -1, -1}, +++ +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x800000ff, Count: 1}}, 31, -1, -1}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x800000ff, Count: 1}}, 32, -1, -1}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x800000ff, Count: 1}}, 33, 4, 1}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1}}, 33, -1, -1}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1}}, 34, 4, 2}, +++ +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 55, 6, 7}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 56, -1, -1}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 63, -1, -1}, +++ +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 64, 8, 0}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 95, 11, 7}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC00000ff, Count: 1, Next: &Sequence{Block: 0x0, Count: 1}}}, 96, -1, -1}, +++ } +++ +++ for n, i := range input { +++ bytePos, bitPos, _ := CheckIfAvailable(i.head, i.ordinal) +++ if bytePos != i.bytePos || bitPos != i.bitPos { +++ t.Fatalf("Error in (%d) checkIfAvailable(ord:%d). Expected (%d, %d). Got (%d, %d)", n, i.ordinal, i.bytePos, i.bitPos, bytePos, bitPos) +++ } +++ } +++} +++ +++func TestMergeSequences(t *testing.T) { +++ input := []struct { +++ original *Sequence +++ merged *Sequence +++ }{ +++ {&Sequence{Block: 0xFE000000, Count: 8, Next: &Sequence{Block: 0xFE000000, Count: 2}}, &Sequence{Block: 0xFE000000, Count: 10}}, +++ {&Sequence{Block: 0xFFFFFFFF, Count: 8, Next: &Sequence{Block: 0xFFFFFFFF, Count: 1}}, &Sequence{Block: 0xFFFFFFFF, Count: 9}}, +++ {&Sequence{Block: 0xFFFFFFFF, Count: 1, Next: &Sequence{Block: 0xFFFFFFFF, Count: 8}}, &Sequence{Block: 0xFFFFFFFF, Count: 9}}, +++ +++ {&Sequence{Block: 0xFFFFFFF0, Count: 8, Next: &Sequence{Block: 0xFFFFFFF0, Count: 1}}, &Sequence{Block: 0xFFFFFFF0, Count: 9}}, +++ {&Sequence{Block: 0xFFFFFFF0, Count: 1, Next: &Sequence{Block: 0xFFFFFFF0, Count: 8}}, &Sequence{Block: 0xFFFFFFF0, Count: 9}}, +++ +++ {&Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xFE, Count: 1, Next: &Sequence{Block: 0xFE, Count: 5}}}, &Sequence{Block: 0xFE, Count: 14}}, +++ {&Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xFE, Count: 1, Next: &Sequence{Block: 0xFE, Count: 5, Next: &Sequence{Block: 0xFF, Count: 1}}}}, +++ &Sequence{Block: 0xFE, Count: 14, Next: &Sequence{Block: 0xFF, Count: 1}}}, +++ +++ // No merge +++ {&Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xF8, Count: 1, Next: &Sequence{Block: 0xFE, Count: 5}}}, +++ &Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xF8, Count: 1, Next: &Sequence{Block: 0xFE, Count: 5}}}}, +++ +++ // No merge from head: // Merge function tries to merge from passed head. If it can't merge with Next, it does not reattempt with Next as head +++ {&Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xFF, Count: 1, Next: &Sequence{Block: 0xFF, Count: 5}}}, +++ &Sequence{Block: 0xFE, Count: 8, Next: &Sequence{Block: 0xFF, Count: 6}}}, +++ } +++ +++ for n, i := range input { +++ mergeSequences(i.original) +++ for !i.merged.Equal(i.original) { +++ t.Fatalf("Error in (%d) mergeSequences().\nExp: %s\nGot: %s,", n, i.merged, i.original) +++ } +++ } +++} +++ +++func TestPushReservation(t *testing.T) { +++ input := []struct { +++ mask *Sequence +++ bytePos int +++ bitPos int +++ newMask *Sequence +++ }{ +++ // Create first Sequence and fill in 8 addresses starting from address 0 +++ {&Sequence{Block: 0x0, Count: 8, Next: nil}, 0, 0, &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 7, Next: nil}}}, +++ {&Sequence{Block: 0x80000000, Count: 8}, 0, 1, &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0x80000000, Count: 7, Next: nil}}}, +++ {&Sequence{Block: 0xC0000000, Count: 8}, 0, 2, &Sequence{Block: 0xE0000000, Count: 1, Next: &Sequence{Block: 0xC0000000, Count: 7, Next: nil}}}, +++ {&Sequence{Block: 0xE0000000, Count: 8}, 0, 3, &Sequence{Block: 0xF0000000, Count: 1, Next: &Sequence{Block: 0xE0000000, Count: 7, Next: nil}}}, +++ {&Sequence{Block: 0xF0000000, Count: 8}, 0, 4, &Sequence{Block: 0xF8000000, Count: 1, Next: &Sequence{Block: 0xF0000000, Count: 7, Next: nil}}}, +++ {&Sequence{Block: 0xF8000000, Count: 8}, 0, 5, &Sequence{Block: 0xFC000000, Count: 1, Next: &Sequence{Block: 0xF8000000, Count: 7, Next: nil}}}, +++ {&Sequence{Block: 0xFC000000, Count: 8}, 0, 6, &Sequence{Block: 0xFE000000, Count: 1, Next: &Sequence{Block: 0xFC000000, Count: 7, Next: nil}}}, +++ {&Sequence{Block: 0xFE000000, Count: 8}, 0, 7, &Sequence{Block: 0xFF000000, Count: 1, Next: &Sequence{Block: 0xFE000000, Count: 7, Next: nil}}}, +++ +++ {&Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 7}}, 0, 1, &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 7, Next: nil}}}, +++ +++ // Create second Sequence and fill in 8 addresses starting from address 32 +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x00000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6, Next: nil}}}, 4, 0, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 1, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 2, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xE0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xE0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 3, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF0000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 4, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF8000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xF8000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 5, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFC000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFC000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 6, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFE000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFE000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 4, 7, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ // fill in 8 addresses starting from address 40 +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF000000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 0, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF800000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFF800000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 1, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFC00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFC00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 2, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFE00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFE00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 3, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF00000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 4, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF80000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFF80000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 5, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFC0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFC0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 6, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFE0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFE0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}, 5, 7, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xFFFF0000, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 6}}}}, +++ +++ // Insert new Sequence +++ {&Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0x0, Count: 6}}, 8, 0, +++ &Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 5}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0x80000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 5}}}, 8, 1, +++ &Sequence{Block: 0xffffffff, Count: 2, Next: &Sequence{Block: 0xC0000000, Count: 1, Next: &Sequence{Block: 0x0, Count: 5}}}}, +++ +++ // Merge affected with Next +++ {&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 2, Next: &Sequence{Block: 0xffffffff, Count: 1}}}, 31, 7, +++ &Sequence{Block: 0xffffffff, Count: 8, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 1}}}}, +++ {&Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xfffffffc, Count: 1, Next: &Sequence{Block: 0xfffffffe, Count: 6}}}, 7, 6, +++ &Sequence{Block: 0xffffffff, Count: 1, Next: &Sequence{Block: 0xfffffffe, Count: 7}}}, +++ +++ // Merge affected with Next and Next.Next +++ {&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 1}}}, 31, 7, +++ &Sequence{Block: 0xffffffff, Count: 9}}, +++ {&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 1}}, 31, 7, +++ &Sequence{Block: 0xffffffff, Count: 8}}, +++ +++ // Merge affected with previous and Next +++ {&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 1}}}, 31, 7, +++ &Sequence{Block: 0xffffffff, Count: 9}}, +++ +++ // Redundant push: No change +++ {&Sequence{Block: 0xffff0000, Count: 1}, 0, 0, &Sequence{Block: 0xffff0000, Count: 1}}, +++ {&Sequence{Block: 0xffff0000, Count: 7}, 25, 7, &Sequence{Block: 0xffff0000, Count: 7}}, +++ {&Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 1}}}, 7, 7, +++ &Sequence{Block: 0xffffffff, Count: 7, Next: &Sequence{Block: 0xfffffffe, Count: 1, Next: &Sequence{Block: 0xffffffff, Count: 1}}}}, +++ } +++ +++ for n, i := range input { +++ mask := PushReservation(i.bytePos, i.bitPos, i.mask, false) +++ if !mask.Equal(i.newMask) { +++ t.Fatalf("Error in (%d) pushReservation():\n%s + (%d,%d):\nExp: %s\nGot: %s,", n, i.mask, i.bytePos, i.bitPos, i.newMask, mask) +++ } +++ } +++} +++ +++func TestSerializeDeserialize(t *testing.T) { +++ s := &Sequence{ +++ Block: 0xffffffff, +++ Count: 1, +++ Next: &Sequence{ +++ Block: 0xFF000000, +++ Count: 1, +++ Next: &Sequence{ +++ Block: 0xffffffff, +++ Count: 6, +++ Next: &Sequence{ +++ Block: 0xffffffff, +++ Count: 1, +++ Next: &Sequence{ +++ Block: 0xFF800000, +++ Count: 1, +++ Next: &Sequence{ +++ Block: 0xffffffff, +++ Count: 6, +++ }, +++ }, +++ }, +++ }, +++ }, +++ } +++ +++ data, err := s.ToByteArray() +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ r := &Sequence{} +++ err = r.FromByteArray(data) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ if !s.Equal(r) { +++ t.Fatalf("Sequences are different: \n%v\n%v", s, r) +++ } +++} diff --cc libnetwork/bitseq/store.go index 00000000,00000000,00000000..b5b3f231 new file mode 100644 --- /dev/null +++ b/libnetwork/bitseq/store.go @@@@ -1,0 -1,0 -1,0 +1,138 @@@@ +++package bitseq +++ +++import ( +++ "encoding/json" +++ "fmt" +++ +++ log "github.com/Sirupsen/logrus" +++ "github.com/docker/libnetwork/datastore" +++ "github.com/docker/libnetwork/types" +++) +++ +++// Key provides the Key to be used in KV Store +++func (h *Handle) Key() []string { +++ h.Lock() +++ defer h.Unlock() +++ return []string{h.app, h.id} +++} +++ +++// KeyPrefix returns the immediate parent key that can be used for tree walk +++func (h *Handle) KeyPrefix() []string { +++ h.Lock() +++ defer h.Unlock() +++ return []string{h.app} +++} +++ +++// Value marshals the data to be stored in the KV store +++func (h *Handle) Value() []byte { +++ b, err := h.ToByteArray() +++ if err != nil { +++ log.Warnf("Failed to serialize Handle: %v", err) +++ b = []byte{} +++ } +++ jv, err := json.Marshal(b) +++ if err != nil { +++ log.Warnf("Failed to json encode bitseq handler byte array: %v", err) +++ return []byte{} +++ } +++ return jv +++} +++ +++// SetValue unmarshals the data from the KV store +++func (h *Handle) SetValue(value []byte) error { +++ var b []byte +++ if err := json.Unmarshal(value, &b); err != nil { +++ return err +++ } +++ +++ return h.FromByteArray(b) +++} +++ +++// Index returns the latest DB Index as seen by this object +++func (h *Handle) Index() uint64 { +++ h.Lock() +++ defer h.Unlock() +++ return h.dbIndex +++} +++ +++// SetIndex method allows the datastore to store the latest DB Index into this object +++func (h *Handle) SetIndex(index uint64) { +++ h.Lock() +++ h.dbIndex = index +++ h.dbExists = true +++ h.Unlock() +++} +++ +++// Exists method is true if this object has been stored in the DB. +++func (h *Handle) Exists() bool { +++ h.Lock() +++ defer h.Unlock() +++ return h.dbExists +++} +++ +++func (h *Handle) watchForChanges() error { +++ h.Lock() +++ store := h.store +++ h.Unlock() +++ +++ if store == nil { +++ return nil +++ } +++ +++ kvpChan, err := store.KVStore().Watch(datastore.Key(h.Key()...), nil) +++ if err != nil { +++ return err +++ } +++ go func() { +++ for { +++ select { +++ case kvPair := <-kvpChan: +++ // Only process remote update +++ if kvPair != nil && (kvPair.LastIndex != h.Index()) { +++ err := h.fromDsValue(kvPair.Value) +++ if err != nil { +++ log.Warnf("Failed to reconstruct bitseq handle from ds watch: %s", err.Error()) +++ } else { +++ h.SetIndex(kvPair.LastIndex) +++ } +++ } +++ } +++ } +++ }() +++ return nil +++} +++ +++func (h *Handle) fromDsValue(value []byte) error { +++ var ba []byte +++ if err := json.Unmarshal(value, &ba); err != nil { +++ return fmt.Errorf("failed to decode json: %s", err.Error()) +++ } +++ if err := h.FromByteArray(ba); err != nil { +++ return fmt.Errorf("failed to decode handle: %s", err.Error()) +++ } +++ return nil +++} +++ +++func (h *Handle) writeToStore() error { +++ h.Lock() +++ store := h.store +++ h.Unlock() +++ if store == nil { +++ return nil +++ } +++ err := store.PutObjectAtomic(h) +++ if err == datastore.ErrKeyModified { +++ return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err) +++ } +++ return err +++} +++ +++func (h *Handle) deleteFromStore() error { +++ h.Lock() +++ store := h.store +++ h.Unlock() +++ if store == nil { +++ return nil +++ } +++ return store.DeleteObjectAtomic(h) +++} diff --cc libnetwork/circle.yml index 00000000,00000000,00000000..d02f6a92 new file mode 100644 --- /dev/null +++ b/libnetwork/circle.yml @@@@ -1,0 -1,0 -1,0 +1,12 @@@@ +++machine: +++ services: +++ - docker +++ +++dependencies: +++ override: +++ - echo "Nothing to install" +++ +++test: +++ override: +++ - make circle-ci +++ diff --cc libnetwork/client/client.go index 00000000,00000000,00000000..c5713b01 new file mode 100644 --- /dev/null +++ b/libnetwork/client/client.go @@@@ -1,0 -1,0 -1,0 +1,115 @@@@ +++package client +++ +++import ( +++ "fmt" +++ "io" +++ "io/ioutil" +++ "net/http" +++ "reflect" +++ "strings" +++ +++ flag "github.com/docker/docker/pkg/mflag" +++) +++ +++// CallFunc provides environment specific call utility to invoke backend functions from UI +++type CallFunc func(string, string, interface{}, map[string][]string) (io.ReadCloser, http.Header, int, error) +++ +++// NetworkCli is the UI object for network subcmds +++type NetworkCli struct { +++ out io.Writer +++ err io.Writer +++ call CallFunc +++} +++ +++// NewNetworkCli is a convenient function to create a NetworkCli object +++func NewNetworkCli(out, err io.Writer, call CallFunc) *NetworkCli { +++ return &NetworkCli{ +++ out: out, +++ err: err, +++ call: call, +++ } +++} +++ +++// getMethod is Borrowed from Docker UI which uses reflection to identify the UI Handler +++func (cli *NetworkCli) getMethod(args ...string) (func(string, ...string) error, bool) { +++ camelArgs := make([]string, len(args)) +++ for i, s := range args { +++ if len(s) == 0 { +++ return nil, false +++ } +++ camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) +++ } +++ methodName := "Cmd" + strings.Join(camelArgs, "") +++ method := reflect.ValueOf(cli).MethodByName(methodName) +++ if !method.IsValid() { +++ return nil, false +++ } +++ return method.Interface().(func(string, ...string) error), true +++} +++ +++// Cmd is borrowed from Docker UI and acts as the entry point for network UI commands. +++// network UI commands are designed to be invoked from multiple parent chains +++func (cli *NetworkCli) Cmd(chain string, args ...string) error { +++ if len(args) > 2 { +++ method, exists := cli.getMethod(args[:3]...) +++ if exists { +++ return method(chain+" "+args[0]+" "+args[1], args[3:]...) +++ } +++ } +++ if len(args) > 1 { +++ method, exists := cli.getMethod(args[:2]...) +++ if exists { +++ return method(chain+" "+args[0], args[2:]...) +++ } +++ } +++ if len(args) > 0 { +++ method, exists := cli.getMethod(args[0]) +++ if !exists { +++ return fmt.Errorf("%s: '%s' is not a %s command. See '%s --help'.\n", chain, args[0], chain, chain) +++ } +++ return method(chain, args[1:]...) +++ } +++ flag.Usage() +++ return nil +++} +++ +++// Subcmd is borrowed from Docker UI and performs the same function of configuring the subCmds +++func (cli *NetworkCli) Subcmd(chain, name, signature, description string, exitOnError bool) *flag.FlagSet { +++ var errorHandling flag.ErrorHandling +++ if exitOnError { +++ errorHandling = flag.ExitOnError +++ } else { +++ errorHandling = flag.ContinueOnError +++ } +++ flags := flag.NewFlagSet(name, errorHandling) +++ flags.Usage = func() { +++ flags.ShortUsage() +++ flags.PrintDefaults() +++ } +++ flags.ShortUsage = func() { +++ options := "" +++ if signature != "" { +++ signature = " " + signature +++ } +++ if flags.FlagCountUndeprecated() > 0 { +++ options = " [OPTIONS]" +++ } +++ fmt.Fprintf(cli.out, "\nUsage: %s %s%s%s\n\n%s\n\n", chain, name, options, signature, description) +++ flags.SetOutput(cli.out) +++ } +++ return flags +++} +++ +++func readBody(stream io.ReadCloser, hdr http.Header, statusCode int, err error) ([]byte, int, error) { +++ if stream != nil { +++ defer stream.Close() +++ } +++ if err != nil { +++ return nil, statusCode, err +++ } +++ body, err := ioutil.ReadAll(stream) +++ if err != nil { +++ return nil, -1, err +++ } +++ return body, statusCode, nil +++} diff --cc libnetwork/client/client_service_test.go index 00000000,00000000,00000000..fb10fd9f new file mode 100644 --- /dev/null +++ b/libnetwork/client/client_service_test.go @@@@ -1,0 -1,0 -1,0 +1,122 @@@@ +++package client +++ +++import ( +++ "bytes" +++ "testing" +++ +++ _ "github.com/docker/libnetwork/netutils" +++) +++ +++func TestClientServiceInvalidCommand(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "service", "invalid") +++ if err == nil { +++ t.Fatalf("Passing invalid commands must fail") +++ } +++} +++ +++func TestClientServiceCreate(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "service", "publish", mockServiceName+"."+mockNwName) +++ if err != nil { +++ t.Fatal(err) +++ } +++} +++ +++func TestClientServiceRm(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "service", "unpublish", mockServiceName+"."+mockNwName) +++ if err != nil { +++ t.Fatal(err) +++ } +++} +++ +++func TestClientServiceLs(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "service", "ls") +++ if err != nil { +++ t.Fatal(err) +++ } +++} +++ +++func TestClientServiceInfo(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "service", "info", mockServiceName+"."+mockNwName) +++ if err != nil { +++ t.Fatal(err) +++ } +++} +++ +++func TestClientServiceInfoById(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "service", "info", mockServiceID+"."+mockNwName) +++ if err != nil { +++ t.Fatal(err) +++ } +++} +++ +++func TestClientServiceJoin(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "service", "attach", mockContainerID, mockServiceName+"."+mockNwName) +++ if err != nil { +++ t.Fatal(err) +++ } +++} +++ +++func TestClientServiceLeave(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "service", "detach", mockContainerID, mockServiceName+"."+mockNwName) +++ if err != nil { +++ t.Fatal(err) +++ } +++} +++ +++// Docker Flag processing in flag.go uses os.Exit() frequently, even for --help +++// TODO : Handle the --help test-case in the IT when CLI is available +++/* +++func TestClientNetworkServiceCreateHelp(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { +++ return nil, 0, nil +++ } +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "create", "--help") +++ if err != nil { +++ t.Fatalf(err.Error()) +++ } +++} +++*/ +++ +++// Docker flag processing in flag.go uses os.Exit(1) for incorrect parameter case. +++// TODO : Handle the missing argument case in the IT when CLI is available +++/* +++func TestClientNetworkServiceCreateMissingArgument(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { +++ return nil, 0, nil +++ } +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "create") +++ if err != nil { +++ t.Fatal(err.Error()) +++ } +++} +++*/ diff --cc libnetwork/client/client_test.go index 00000000,00000000,00000000..06b8b2a6 new file mode 100644 --- /dev/null +++ b/libnetwork/client/client_test.go @@@@ -1,0 -1,0 -1,0 +1,217 @@@@ +++package client +++ +++import ( +++ "bytes" +++ "encoding/json" +++ "fmt" +++ "io" +++ "net/http" +++ "os" +++ "strings" +++ "testing" +++ +++ _ "github.com/docker/libnetwork/netutils" +++) +++ +++// nopCloser is used to provide a dummy CallFunc for Cmd() +++type nopCloser struct { +++ io.Reader +++} +++ +++func (nopCloser) Close() error { return nil } +++ +++func TestMain(m *testing.M) { +++ setupMockHTTPCallback() +++ os.Exit(m.Run()) +++} +++ +++var callbackFunc func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) +++var mockNwJSON, mockNwListJSON, mockServiceJSON, mockServiceListJSON []byte +++var mockNwName = "test" +++var mockNwID = "2a3456789" +++var mockServiceName = "testSrv" +++var mockServiceID = "2a3456789" +++var mockContainerID = "2a3456789" +++ +++func setupMockHTTPCallback() { +++ var list []networkResource +++ nw := networkResource{Name: mockNwName, ID: mockNwID} +++ mockNwJSON, _ = json.Marshal(nw) +++ list = append(list, nw) +++ mockNwListJSON, _ = json.Marshal(list) +++ +++ var srvList []serviceResource +++ ep := serviceResource{Name: mockServiceName, ID: mockServiceID, Network: mockNwName} +++ mockServiceJSON, _ = json.Marshal(ep) +++ srvList = append(srvList, ep) +++ mockServiceListJSON, _ = json.Marshal(srvList) +++ +++ dummyHTTPHdr := http.Header{} +++ +++ callbackFunc = func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) { +++ var rsp string +++ switch method { +++ case "GET": +++ if strings.Contains(path, fmt.Sprintf("networks?name=%s", mockNwName)) { +++ rsp = string(mockNwListJSON) +++ } else if strings.Contains(path, "networks?name=") { +++ rsp = "[]" +++ } else if strings.Contains(path, fmt.Sprintf("networks?partial-id=%s", mockNwID)) { +++ rsp = string(mockNwListJSON) +++ } else if strings.Contains(path, "networks?partial-id=") { +++ rsp = "[]" +++ } else if strings.HasSuffix(path, "networks") { +++ rsp = string(mockNwListJSON) +++ } else if strings.HasSuffix(path, "networks/"+mockNwID) { +++ rsp = string(mockNwJSON) +++ } else if strings.Contains(path, fmt.Sprintf("services?name=%s", mockServiceName)) { +++ rsp = string(mockServiceListJSON) +++ } else if strings.Contains(path, "services?name=") { +++ rsp = "[]" +++ } else if strings.Contains(path, fmt.Sprintf("services?partial-id=%s", mockServiceID)) { +++ rsp = string(mockServiceListJSON) +++ } else if strings.Contains(path, "services?partial-id=") { +++ rsp = "[]" +++ } else if strings.HasSuffix(path, "services") { +++ rsp = string(mockServiceListJSON) +++ } else if strings.HasSuffix(path, "services/"+mockServiceID) { +++ rsp = string(mockServiceJSON) +++ } else if strings.Contains(path, "containers") { +++ return nopCloser{bytes.NewBufferString("")}, dummyHTTPHdr, 400, fmt.Errorf("Bad Request") +++ } +++ case "POST": +++ var data []byte +++ if strings.HasSuffix(path, "networks") { +++ data, _ = json.Marshal(mockNwID) +++ } else if strings.HasSuffix(path, "services") { +++ data, _ = json.Marshal(mockServiceID) +++ } else if strings.HasSuffix(path, "backend") { +++ data, _ = json.Marshal(mockContainerID) +++ } +++ rsp = string(data) +++ case "PUT": +++ case "DELETE": +++ rsp = "" +++ } +++ return nopCloser{bytes.NewBufferString(rsp)}, dummyHTTPHdr, 200, nil +++ } +++} +++ +++func TestClientDummyCommand(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "dummy") +++ if err == nil { +++ t.Fatalf("Incorrect Command must fail") +++ } +++} +++ +++func TestClientNetworkInvalidCommand(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "invalid") +++ if err == nil { +++ t.Fatalf("Passing invalid commands must fail") +++ } +++} +++ +++func TestClientNetworkCreate(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "create", mockNwName) +++ if err != nil { +++ t.Fatal(err.Error()) +++ } +++} +++ +++func TestClientNetworkCreateWithDriver(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "create", "-f=dummy", mockNwName) +++ if err == nil { +++ t.Fatalf("Passing incorrect flags to the create command must fail") +++ } +++ +++ err = cli.Cmd("docker", "network", "create", "-d=dummy", mockNwName) +++ if err != nil { +++ t.Fatalf(err.Error()) +++ } +++} +++ +++func TestClientNetworkRm(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "rm", mockNwName) +++ if err != nil { +++ t.Fatal(err.Error()) +++ } +++} +++ +++func TestClientNetworkLs(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "ls") +++ if err != nil { +++ t.Fatal(err.Error()) +++ } +++} +++ +++func TestClientNetworkInfo(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "info", mockNwName) +++ if err != nil { +++ t.Fatal(err.Error()) +++ } +++} +++ +++func TestClientNetworkInfoById(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "info", mockNwID) +++ if err != nil { +++ t.Fatal(err.Error()) +++ } +++} +++ +++// Docker Flag processing in flag.go uses os.Exit() frequently, even for --help +++// TODO : Handle the --help test-case in the IT when CLI is available +++/* +++func TestClientNetworkServiceCreateHelp(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { +++ return nil, 0, nil +++ } +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "create", "--help") +++ if err != nil { +++ t.Fatalf(err.Error()) +++ } +++} +++*/ +++ +++// Docker flag processing in flag.go uses os.Exit(1) for incorrect parameter case. +++// TODO : Handle the missing argument case in the IT when CLI is available +++/* +++func TestClientNetworkServiceCreateMissingArgument(t *testing.T) { +++ var out, errOut bytes.Buffer +++ cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) { +++ return nil, 0, nil +++ } +++ cli := NewNetworkCli(&out, &errOut, callbackFunc) +++ +++ err := cli.Cmd("docker", "network", "create") +++ if err != nil { +++ t.Fatal(err.Error()) +++ } +++} +++*/ diff --cc libnetwork/client/network.go index 00000000,00000000,00000000..a244ad5f new file mode 100644 --- /dev/null +++ b/libnetwork/client/network.go @@@@ -1,0 -1,0 -1,0 +1,231 @@@@ +++package client +++ +++import ( +++ "bytes" +++ "encoding/json" +++ "fmt" +++ "net/http" +++ "text/tabwriter" +++ +++ flag "github.com/docker/docker/pkg/mflag" +++ "github.com/docker/docker/pkg/stringid" +++) +++ +++type command struct { +++ name string +++ description string +++} +++ +++var ( +++ networkCommands = []command{ +++ {"create", "Create a network"}, +++ {"rm", "Remove a network"}, +++ {"ls", "List all networks"}, +++ {"info", "Display information of a network"}, +++ } +++) +++ +++// CmdNetwork handles the root Network UI +++func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "network", "COMMAND [OPTIONS] [arg...]", networkUsage(chain), false) +++ cmd.Require(flag.Min, 1) +++ err := cmd.ParseFlags(args, true) +++ if err == nil { +++ cmd.Usage() +++ return fmt.Errorf("invalid command : %v", args) +++ } +++ return err +++} +++ +++// CmdNetworkCreate handles Network Create UI +++func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "create", "NETWORK-NAME", "Creates a new network with a name specified by the user", false) +++ flDriver := cmd.String([]string{"d", "-driver"}, "", "Driver to manage the Network") +++ cmd.Require(flag.Exact, 1) +++ err := cmd.ParseFlags(args, true) +++ if err != nil { +++ return err +++ } +++ +++ // Construct network create request body +++ ops := make(map[string]interface{}) +++ nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, Options: ops} +++ obj, _, err := readBody(cli.call("POST", "/networks", nc, nil)) +++ if err != nil { +++ return err +++ } +++ var replyID string +++ err = json.Unmarshal(obj, &replyID) +++ if err != nil { +++ return err +++ } +++ fmt.Fprintf(cli.out, "%s\n", replyID) +++ return nil +++} +++ +++// CmdNetworkRm handles Network Delete UI +++func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "rm", "NETWORK", "Deletes a network", false) +++ cmd.Require(flag.Exact, 1) +++ err := cmd.ParseFlags(args, true) +++ if err != nil { +++ return err +++ } +++ id, err := lookupNetworkID(cli, cmd.Arg(0)) +++ if err != nil { +++ return err +++ } +++ _, _, err = readBody(cli.call("DELETE", "/networks/"+id, nil, nil)) +++ if err != nil { +++ return err +++ } +++ return nil +++} +++ +++// CmdNetworkLs handles Network List UI +++func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "ls", "", "Lists all the networks created by the user", false) +++ quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") +++ noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output") +++ nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created") +++ last := cmd.Int([]string{"n"}, -1, "Show n last created networks") +++ err := cmd.ParseFlags(args, true) +++ if err != nil { +++ return err +++ } +++ obj, _, err := readBody(cli.call("GET", "/networks", nil, nil)) +++ if err != nil { +++ return err +++ } +++ if *last == -1 && *nLatest { +++ *last = 1 +++ } +++ +++ var networkResources []networkResource +++ err = json.Unmarshal(obj, &networkResources) +++ if err != nil { +++ return err +++ } +++ +++ wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) +++ +++ // unless quiet (-q) is specified, print field titles +++ if !*quiet { +++ fmt.Fprintln(wr, "NETWORK ID\tNAME\tTYPE") +++ } +++ +++ for _, networkResource := range networkResources { +++ ID := networkResource.ID +++ netName := networkResource.Name +++ if !*noTrunc { +++ ID = stringid.TruncateID(ID) +++ } +++ if *quiet { +++ fmt.Fprintln(wr, ID) +++ continue +++ } +++ netType := networkResource.Type +++ fmt.Fprintf(wr, "%s\t%s\t%s\t", +++ ID, +++ netName, +++ netType) +++ fmt.Fprint(wr, "\n") +++ } +++ wr.Flush() +++ return nil +++} +++ +++// CmdNetworkInfo handles Network Info UI +++func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "info", "NETWORK", "Displays detailed information on a network", false) +++ cmd.Require(flag.Exact, 1) +++ err := cmd.ParseFlags(args, true) +++ if err != nil { +++ return err +++ } +++ +++ id, err := lookupNetworkID(cli, cmd.Arg(0)) +++ if err != nil { +++ return err +++ } +++ +++ obj, _, err := readBody(cli.call("GET", "/networks/"+id, nil, nil)) +++ if err != nil { +++ return err +++ } +++ networkResource := &networkResource{} +++ if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil { +++ return err +++ } +++ fmt.Fprintf(cli.out, "Network Id: %s\n", networkResource.ID) +++ fmt.Fprintf(cli.out, "Name: %s\n", networkResource.Name) +++ fmt.Fprintf(cli.out, "Type: %s\n", networkResource.Type) +++ if networkResource.Services != nil { +++ for _, serviceResource := range networkResource.Services { +++ fmt.Fprintf(cli.out, " Service Id: %s\n", serviceResource.ID) +++ fmt.Fprintf(cli.out, "\tName: %s\n", serviceResource.Name) +++ } +++ } +++ +++ return nil +++} +++ +++// Helper function to predict if a string is a name or id or partial-id +++// This provides a best-effort mechanism to identify a id with the help of GET Filter APIs +++// Being a UI, its most likely that name will be used by the user, which is used to lookup +++// the corresponding ID. If ID is not found, this function will assume that the passed string +++// is an ID by itself. +++ +++func lookupNetworkID(cli *NetworkCli, nameID string) (string, error) { +++ obj, statusCode, err := readBody(cli.call("GET", "/networks?name="+nameID, nil, nil)) +++ if err != nil { +++ return "", err +++ } +++ +++ if statusCode != http.StatusOK { +++ return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj)) +++ } +++ +++ var list []*networkResource +++ err = json.Unmarshal(obj, &list) +++ if err != nil { +++ return "", err +++ } +++ if len(list) > 0 { +++ // name query filter will always return a single-element collection +++ return list[0].ID, nil +++ } +++ +++ // Check for Partial-id +++ obj, statusCode, err = readBody(cli.call("GET", "/networks?partial-id="+nameID, nil, nil)) +++ if err != nil { +++ return "", err +++ } +++ +++ if statusCode != http.StatusOK { +++ return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj)) +++ } +++ +++ err = json.Unmarshal(obj, &list) +++ if err != nil { +++ return "", err +++ } +++ if len(list) == 0 { +++ return "", fmt.Errorf("resource not found %s", nameID) +++ } +++ if len(list) > 1 { +++ return "", fmt.Errorf("multiple Networks matching the partial identifier (%s). Please use full identifier", nameID) +++ } +++ return list[0].ID, nil +++} +++ +++func networkUsage(chain string) string { +++ help := "Commands:\n" +++ +++ for _, cmd := range networkCommands { +++ help += fmt.Sprintf(" %-25.25s%s\n", cmd.name, cmd.description) +++ } +++ +++ help += fmt.Sprintf("\nRun '%s network COMMAND --help' for more information on a command.", chain) +++ return help +++} diff --cc libnetwork/client/service.go index 00000000,00000000,00000000..35e040f8 new file mode 100644 --- /dev/null +++ b/libnetwork/client/service.go @@@@ -1,0 -1,0 -1,0 +1,362 @@@@ +++package client +++ +++import ( +++ "bytes" +++ "encoding/json" +++ "fmt" +++ "net/http" +++ "strings" +++ "text/tabwriter" +++ +++ flag "github.com/docker/docker/pkg/mflag" +++ "github.com/docker/docker/pkg/stringid" +++) +++ +++var ( +++ serviceCommands = []command{ +++ {"publish", "Publish a service"}, +++ {"unpublish", "Remove a service"}, +++ {"attach", "Attach a backend (container) to the service"}, +++ {"detach", "Detach the backend from the service"}, +++ {"ls", "Lists all services"}, +++ {"info", "Display information about a service"}, +++ } +++) +++ +++func lookupServiceID(cli *NetworkCli, nwName, svNameID string) (string, error) { +++ // Sanity Check +++ obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/networks?name=%s", nwName), nil, nil)) +++ if err != nil { +++ return "", err +++ } +++ var nwList []networkResource +++ if err = json.Unmarshal(obj, &nwList); err != nil { +++ return "", err +++ } +++ if len(nwList) == 0 { +++ return "", fmt.Errorf("Network %s does not exist", nwName) +++ } +++ +++ if nwName == "" { +++ obj, _, err := readBody(cli.call("GET", "/networks/"+nwList[0].ID, nil, nil)) +++ if err != nil { +++ return "", err +++ } +++ networkResource := &networkResource{} +++ if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil { +++ return "", err +++ } +++ nwName = networkResource.Name +++ } +++ +++ // Query service by name +++ obj, statusCode, err := readBody(cli.call("GET", fmt.Sprintf("/services?name=%s", svNameID), nil, nil)) +++ if err != nil { +++ return "", err +++ } +++ +++ if statusCode != http.StatusOK { +++ return "", fmt.Errorf("name query failed for %s due to: (%d) %s", svNameID, statusCode, string(obj)) +++ } +++ +++ var list []*serviceResource +++ if err = json.Unmarshal(obj, &list); err != nil { +++ return "", err +++ } +++ for _, sr := range list { +++ if sr.Network == nwName { +++ return sr.ID, nil +++ } +++ } +++ +++ // Query service by Partial-id (this covers full id as well) +++ obj, statusCode, err = readBody(cli.call("GET", fmt.Sprintf("/services?partial-id=%s", svNameID), nil, nil)) +++ if err != nil { +++ return "", err +++ } +++ +++ if statusCode != http.StatusOK { +++ return "", fmt.Errorf("partial-id match query failed for %s due to: (%d) %s", svNameID, statusCode, string(obj)) +++ } +++ +++ if err = json.Unmarshal(obj, &list); err != nil { +++ return "", err +++ } +++ for _, sr := range list { +++ if sr.Network == nwName { +++ return sr.ID, nil +++ } +++ } +++ +++ return "", fmt.Errorf("Service %s not found on network %s", svNameID, nwName) +++} +++ +++func lookupContainerID(cli *NetworkCli, cnNameID string) (string, error) { +++ // Container is a Docker resource, ask docker about it. +++ // In case of connecton error, we assume we are running in dnet and return whatever was passed to us +++ obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/containers/%s/json", cnNameID), nil, nil)) +++ if err != nil { +++ // We are probably running outside of docker +++ return cnNameID, nil +++ } +++ +++ var x map[string]interface{} +++ err = json.Unmarshal(obj, &x) +++ if err != nil { +++ return "", err +++ } +++ if iid, ok := x["Id"]; ok { +++ if id, ok := iid.(string); ok { +++ return id, nil +++ } +++ return "", fmt.Errorf("Unexpected data type for container ID in json response") +++ } +++ return "", fmt.Errorf("Cannot find container ID in json response") +++} +++ +++// CmdService handles the service UI +++func (cli *NetworkCli) CmdService(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "service", "COMMAND [OPTIONS] [arg...]", serviceUsage(chain), false) +++ cmd.Require(flag.Min, 1) +++ err := cmd.ParseFlags(args, true) +++ if err == nil { +++ cmd.Usage() +++ return fmt.Errorf("Invalid command : %v", args) +++ } +++ return err +++} +++ +++// Parse service name for "SERVICE[.NETWORK]" format +++func parseServiceName(name string) (string, string) { +++ s := strings.Split(name, ".") +++ var sName, nName string +++ if len(s) > 1 { +++ nName = s[len(s)-1] +++ sName = strings.Join(s[:len(s)-1], ".") +++ } else { +++ sName = s[0] +++ } +++ return sName, nName +++} +++ +++// CmdServicePublish handles service create UI +++func (cli *NetworkCli) CmdServicePublish(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "publish", "SERVICE[.NETWORK]", "Publish a new service on a network", false) +++ cmd.Require(flag.Exact, 1) +++ err := cmd.ParseFlags(args, true) +++ if err != nil { +++ return err +++ } +++ +++ sn, nn := parseServiceName(cmd.Arg(0)) +++ sc := serviceCreate{Name: sn, Network: nn} +++ obj, _, err := readBody(cli.call("POST", "/services", sc, nil)) +++ if err != nil { +++ return err +++ } +++ +++ var replyID string +++ err = json.Unmarshal(obj, &replyID) +++ if err != nil { +++ return err +++ } +++ +++ fmt.Fprintf(cli.out, "%s\n", replyID) +++ return nil +++} +++ +++// CmdServiceUnpublish handles service delete UI +++func (cli *NetworkCli) CmdServiceUnpublish(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "unpublish", "SERVICE[.NETWORK]", "Removes a service", false) +++ cmd.Require(flag.Exact, 1) +++ err := cmd.ParseFlags(args, true) +++ if err != nil { +++ return err +++ } +++ +++ sn, nn := parseServiceName(cmd.Arg(0)) +++ serviceID, err := lookupServiceID(cli, nn, sn) +++ if err != nil { +++ return err +++ } +++ +++ _, _, err = readBody(cli.call("DELETE", "/services/"+serviceID, nil, nil)) +++ +++ return err +++} +++ +++// CmdServiceLs handles service list UI +++func (cli *NetworkCli) CmdServiceLs(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "ls", "SERVICE", "Lists all the services on a network", false) +++ flNetwork := cmd.String([]string{"net", "-network"}, "", "Only show the services that are published on the specified network") +++ quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") +++ noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output") +++ +++ err := cmd.ParseFlags(args, true) +++ if err != nil { +++ return err +++ } +++ +++ var obj []byte +++ if *flNetwork == "" { +++ obj, _, err = readBody(cli.call("GET", "/services", nil, nil)) +++ } else { +++ obj, _, err = readBody(cli.call("GET", "/services?network="+*flNetwork, nil, nil)) +++ } +++ if err != nil { +++ return err +++ } +++ +++ var serviceResources []serviceResource +++ err = json.Unmarshal(obj, &serviceResources) +++ if err != nil { +++ fmt.Println(err) +++ return err +++ } +++ +++ wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) +++ // unless quiet (-q) is specified, print field titles +++ if !*quiet { +++ fmt.Fprintln(wr, "SERVICE ID\tNAME\tNETWORK\tCONTAINER") +++ } +++ +++ for _, sr := range serviceResources { +++ ID := sr.ID +++ bkID, err := getBackendID(cli, ID) +++ if err != nil { +++ return err +++ } +++ if !*noTrunc { +++ ID = stringid.TruncateID(ID) +++ bkID = stringid.TruncateID(bkID) +++ } +++ if !*quiet { +++ fmt.Fprintf(wr, "%s\t%s\t%s\t%s\n", ID, sr.Name, sr.Network, bkID) +++ } else { +++ fmt.Fprintln(wr, ID) +++ } +++ } +++ wr.Flush() +++ +++ return nil +++} +++ +++func getBackendID(cli *NetworkCli, servID string) (string, error) { +++ var ( +++ obj []byte +++ err error +++ bk string +++ ) +++ +++ if obj, _, err = readBody(cli.call("GET", "/services/"+servID+"/backend", nil, nil)); err == nil { +++ var bkl []backendResource +++ if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&bkl); err == nil { +++ if len(bkl) > 0 { +++ bk = bkl[0].ID +++ } +++ } else { +++ // Only print a message, don't make the caller cli fail for this +++ fmt.Fprintf(cli.out, "Failed to retrieve backend list for service %s (%v)", servID, err) +++ } +++ } +++ +++ return bk, err +++} +++ +++// CmdServiceInfo handles service info UI +++func (cli *NetworkCli) CmdServiceInfo(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "info", "SERVICE[.NETWORK]", "Displays detailed information about a service", false) +++ cmd.Require(flag.Min, 1) +++ +++ err := cmd.ParseFlags(args, true) +++ if err != nil { +++ return err +++ } +++ +++ sn, nn := parseServiceName(cmd.Arg(0)) +++ serviceID, err := lookupServiceID(cli, nn, sn) +++ if err != nil { +++ return err +++ } +++ +++ obj, _, err := readBody(cli.call("GET", "/services/"+serviceID, nil, nil)) +++ if err != nil { +++ return err +++ } +++ +++ sr := &serviceResource{} +++ if err := json.NewDecoder(bytes.NewReader(obj)).Decode(sr); err != nil { +++ return err +++ } +++ +++ fmt.Fprintf(cli.out, "Service Id: %s\n", sr.ID) +++ fmt.Fprintf(cli.out, "\tName: %s\n", sr.Name) +++ fmt.Fprintf(cli.out, "\tNetwork: %s\n", sr.Network) +++ +++ return nil +++} +++ +++// CmdServiceAttach handles service attach UI +++func (cli *NetworkCli) CmdServiceAttach(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "attach", "CONTAINER SERVICE[.NETWORK]", "Sets a container as a service backend", false) +++ cmd.Require(flag.Min, 2) +++ err := cmd.ParseFlags(args, true) +++ if err != nil { +++ return err +++ } +++ +++ containerID, err := lookupContainerID(cli, cmd.Arg(0)) +++ if err != nil { +++ return err +++ } +++ +++ sn, nn := parseServiceName(cmd.Arg(1)) +++ serviceID, err := lookupServiceID(cli, nn, sn) +++ if err != nil { +++ return err +++ } +++ +++ nc := serviceAttach{ContainerID: containerID} +++ +++ _, _, err = readBody(cli.call("POST", "/services/"+serviceID+"/backend", nc, nil)) +++ +++ return err +++} +++ +++// CmdServiceDetach handles service detach UI +++func (cli *NetworkCli) CmdServiceDetach(chain string, args ...string) error { +++ cmd := cli.Subcmd(chain, "detach", "CONTAINER SERVICE", "Removes a container from service backend", false) +++ cmd.Require(flag.Min, 2) +++ err := cmd.ParseFlags(args, true) +++ if err != nil { +++ return err +++ } +++ +++ sn, nn := parseServiceName(cmd.Arg(1)) +++ containerID, err := lookupContainerID(cli, cmd.Arg(0)) +++ if err != nil { +++ return err +++ } +++ +++ serviceID, err := lookupServiceID(cli, nn, sn) +++ if err != nil { +++ return err +++ } +++ +++ _, _, err = readBody(cli.call("DELETE", "/services/"+serviceID+"/backend/"+containerID, nil, nil)) +++ if err != nil { +++ return err +++ } +++ return nil +++} +++ +++func serviceUsage(chain string) string { +++ help := "Commands:\n" +++ +++ for _, cmd := range serviceCommands { +++ help += fmt.Sprintf(" %-10.10s%s\n", cmd.name, cmd.description) +++ } +++ +++ help += fmt.Sprintf("\nRun '%s service COMMAND --help' for more information on a command.", chain) +++ return help +++} diff --cc libnetwork/client/types.go index 00000000,00000000,00000000..e14460ab new file mode 100644 --- /dev/null +++ b/libnetwork/client/types.go @@@@ -1,0 -1,0 -1,0 +1,73 @@@@ +++package client +++ +++import "github.com/docker/libnetwork/types" +++ +++/*********** +++ Resources +++************/ +++ +++// networkResource is the body of the "get network" http response message +++type networkResource struct { +++ Name string `json:"name"` +++ ID string `json:"id"` +++ Type string `json:"type"` +++ Services []*serviceResource `json:"services"` +++} +++ +++// serviceResource is the body of the "get service" http response message +++type serviceResource struct { +++ Name string `json:"name"` +++ ID string `json:"id"` +++ Network string `json:"network"` +++} +++ +++// backendResource is the body of "get service backend" response message +++type backendResource struct { +++ ID string `json:"id"` +++} +++ +++/*********** +++ Body types +++ ************/ +++ +++// networkCreate is the expected body of the "create network" http request message +++type networkCreate struct { +++ Name string `json:"name"` +++ NetworkType string `json:"network_type"` +++ Options map[string]interface{} `json:"options"` +++} +++ +++// serviceCreate represents the body of the "publish service" http request message +++type serviceCreate struct { +++ Name string `json:"name"` +++ Network string `json:"network_name"` +++ ExposedPorts []types.TransportPort `json:"exposed_ports"` +++ PortMapping []types.PortBinding `json:"port_mapping"` +++} +++ +++// serviceAttach represents the expected body of the "attach/detach backend to/from service" http request messages +++type serviceAttach struct { +++ ContainerID string `json:"container_id"` +++ HostName string `json:"host_name"` +++ DomainName string `json:"domain_name"` +++ HostsPath string `json:"hosts_path"` +++ ResolvConfPath string `json:"resolv_conf_path"` +++ DNS []string `json:"dns"` +++ ExtraHosts []serviceExtraHost `json:"extra_hosts"` +++ ParentUpdates []serviceParentUpdate `json:"parent_updates"` +++ UseDefaultSandbox bool `json:"use_default_sandbox"` +++} +++ +++// serviceExtraHost represents the extra host object +++type serviceExtraHost struct { +++ Name string `json:"name"` +++ Address string `json:"address"` +++} +++ +++// EndpointParentUpdate is the object carrying the information about the +++// endpoint parent that needs to be updated +++type serviceParentUpdate struct { +++ EndpointID string `json:"service_id"` +++ Name string `json:"name"` +++ Address string `json:"address"` +++} diff --cc libnetwork/cmd/dnet/dnet.go index 00000000,00000000,00000000..c0d497d4 new file mode 100644 --- /dev/null +++ b/libnetwork/cmd/dnet/dnet.go @@@@ -1,0 -1,0 -1,0 +1,288 @@@@ +++package main +++ +++import ( +++ "bytes" +++ "encoding/json" +++ "fmt" +++ "io" +++ "io/ioutil" +++ "net/http" +++ "os" +++ "strings" +++ +++ flag "github.com/docker/docker/pkg/mflag" +++ "github.com/docker/docker/pkg/parsers" +++ "github.com/docker/docker/pkg/reexec" +++ +++ "github.com/Sirupsen/logrus" +++ "github.com/docker/docker/pkg/term" +++ "github.com/docker/libnetwork" +++ "github.com/docker/libnetwork/api" +++ "github.com/docker/libnetwork/client" +++ "github.com/docker/libnetwork/config" +++ "github.com/docker/libnetwork/netlabel" +++ "github.com/docker/libnetwork/options" +++ "github.com/gorilla/mux" +++) +++ +++const ( +++ // DefaultHTTPHost is used if only port is provided to -H flag e.g. docker -d -H tcp://:8080 +++ DefaultHTTPHost = "127.0.0.1" +++ // DefaultHTTPPort is the default http port used by dnet +++ DefaultHTTPPort = 2385 +++ // DefaultUnixSocket exported +++ DefaultUnixSocket = "/var/run/dnet.sock" +++ cfgFileEnv = "LIBNETWORK_CFG" +++ defaultCfgFile = "/etc/default/libnetwork.toml" +++) +++ +++func main() { +++ if reexec.Init() { +++ return +++ } +++ +++ _, stdout, stderr := term.StdStreams() +++ logrus.SetOutput(stderr) +++ +++ err := dnetCommand(stdout, stderr) +++ if err != nil { +++ os.Exit(1) +++ } +++} +++ +++func parseConfig(cfgFile string) (*config.Config, error) { +++ if strings.Trim(cfgFile, " ") == "" { +++ cfgFile = os.Getenv(cfgFileEnv) +++ if strings.Trim(cfgFile, " ") == "" { +++ cfgFile = defaultCfgFile +++ } +++ } +++ return config.ParseConfig(cfgFile) +++} +++ +++func processConfig(cfg *config.Config) []config.Option { +++ options := []config.Option{} +++ if cfg == nil { +++ return options +++ } +++ dn := "bridge" +++ if strings.TrimSpace(cfg.Daemon.DefaultNetwork) != "" { +++ dn = cfg.Daemon.DefaultNetwork +++ } +++ options = append(options, config.OptionDefaultNetwork(dn)) +++ +++ dd := "bridge" +++ if strings.TrimSpace(cfg.Daemon.DefaultDriver) != "" { +++ dd = cfg.Daemon.DefaultDriver +++ } +++ options = append(options, config.OptionDefaultDriver(dd)) +++ +++ if cfg.Daemon.Labels != nil { +++ options = append(options, config.OptionLabels(cfg.Daemon.Labels)) +++ } +++ if strings.TrimSpace(cfg.Datastore.Client.Provider) != "" { +++ options = append(options, config.OptionKVProvider(cfg.Datastore.Client.Provider)) +++ } +++ if strings.TrimSpace(cfg.Datastore.Client.Address) != "" { +++ options = append(options, config.OptionKVProviderURL(cfg.Datastore.Client.Address)) +++ } +++ return options +++} +++ +++func dnetCommand(stdout, stderr io.Writer) error { +++ flag.Parse() +++ +++ if *flHelp { +++ flag.Usage() +++ return nil +++ } +++ +++ if *flLogLevel != "" { +++ lvl, err := logrus.ParseLevel(*flLogLevel) +++ if err != nil { +++ fmt.Fprintf(stderr, "Unable to parse logging level: %s\n", *flLogLevel) +++ return err +++ } +++ logrus.SetLevel(lvl) +++ } else { +++ logrus.SetLevel(logrus.InfoLevel) +++ } +++ +++ if *flDebug { +++ logrus.SetLevel(logrus.DebugLevel) +++ } +++ +++ if *flHost == "" { +++ defaultHost := os.Getenv("DNET_HOST") +++ if defaultHost == "" { +++ // TODO : Add UDS support +++ defaultHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort) +++ } +++ *flHost = defaultHost +++ } +++ +++ dc, err := newDnetConnection(*flHost) +++ if err != nil { +++ if *flDaemon { +++ logrus.Error(err) +++ } else { +++ fmt.Fprint(stderr, err) +++ } +++ return err +++ } +++ +++ if *flDaemon { +++ err := dc.dnetDaemon() +++ if err != nil { +++ logrus.Errorf("dnet Daemon exited with an error : %v", err) +++ } +++ return err +++ } +++ +++ cli := client.NewNetworkCli(stdout, stderr, dc.httpCall) +++ if err := cli.Cmd("dnet", flag.Args()...); err != nil { +++ fmt.Fprintln(stderr, err) +++ return err +++ } +++ return nil +++} +++ +++func createDefaultNetwork(c libnetwork.NetworkController) { +++ nw := c.Config().Daemon.DefaultNetwork +++ d := c.Config().Daemon.DefaultDriver +++ createOptions := []libnetwork.NetworkOption{} +++ genericOption := options.Generic{} +++ +++ if nw != "" && d != "" { +++ // Bridge driver is special due to legacy reasons +++ if d == "bridge" { +++ genericOption[netlabel.GenericData] = map[string]interface{}{ +++ "BridgeName": nw, +++ "AllowNonDefaultBridge": "true", +++ } +++ networkOption := libnetwork.NetworkOptionGeneric(genericOption) +++ createOptions = append(createOptions, networkOption) +++ } +++ _, err := c.NewNetwork(d, nw, createOptions...) +++ if err != nil { +++ logrus.Errorf("Error creating default network : %s : %v", nw, err) +++ } +++ } +++} +++ +++type dnetConnection struct { +++ // proto holds the client protocol i.e. unix. +++ proto string +++ // addr holds the client address. +++ addr string +++} +++ +++func (d *dnetConnection) dnetDaemon() error { +++ cfg, err := parseConfig(*flCfgFile) +++ var cOptions []config.Option +++ if err == nil { +++ cOptions = processConfig(cfg) +++ } +++ controller, err := libnetwork.New(cOptions...) +++ if err != nil { +++ fmt.Println("Error starting dnetDaemon :", err) +++ return err +++ } +++ createDefaultNetwork(controller) +++ httpHandler := api.NewHTTPHandler(controller) +++ r := mux.NewRouter().StrictSlash(false) +++ post := r.PathPrefix("/{.*}/networks").Subrouter() +++ post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler) +++ post = r.PathPrefix("/networks").Subrouter() +++ post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler) +++ post = r.PathPrefix("/{.*}/services").Subrouter() +++ post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler) +++ post = r.PathPrefix("/services").Subrouter() +++ post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler) +++ return http.ListenAndServe(d.addr, r) +++} +++ +++func newDnetConnection(val string) (*dnetConnection, error) { +++ url, err := parsers.ParseHost(DefaultHTTPHost, DefaultUnixSocket, val) +++ if err != nil { +++ return nil, err +++ } +++ protoAddrParts := strings.SplitN(url, "://", 2) +++ if len(protoAddrParts) != 2 { +++ return nil, fmt.Errorf("bad format, expected tcp://ADDR") +++ } +++ if strings.ToLower(protoAddrParts[0]) != "tcp" { +++ return nil, fmt.Errorf("dnet currently only supports tcp transport") +++ } +++ +++ return &dnetConnection{protoAddrParts[0], protoAddrParts[1]}, nil +++} +++ +++func (d *dnetConnection) httpCall(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) { +++ var in io.Reader +++ in, err := encodeData(data) +++ if err != nil { +++ return nil, nil, -1, err +++ } +++ +++ req, err := http.NewRequest(method, fmt.Sprintf("%s", path), in) +++ if err != nil { +++ return nil, nil, -1, err +++ } +++ +++ setupRequestHeaders(method, data, req, headers) +++ +++ req.URL.Host = d.addr +++ req.URL.Scheme = "http" +++ +++ httpClient := &http.Client{} +++ resp, err := httpClient.Do(req) +++ statusCode := -1 +++ if resp != nil { +++ statusCode = resp.StatusCode +++ } +++ if err != nil { +++ return nil, nil, statusCode, fmt.Errorf("error when trying to connect: %v", err) +++ } +++ +++ if statusCode < 200 || statusCode >= 400 { +++ body, err := ioutil.ReadAll(resp.Body) +++ if err != nil { +++ return nil, nil, statusCode, err +++ } +++ return nil, nil, statusCode, fmt.Errorf("error : %s", bytes.TrimSpace(body)) +++ } +++ +++ return resp.Body, resp.Header, statusCode, nil +++} +++ +++func setupRequestHeaders(method string, data interface{}, req *http.Request, headers map[string][]string) { +++ if data != nil { +++ if headers == nil { +++ headers = make(map[string][]string) +++ } +++ headers["Content-Type"] = []string{"application/json"} +++ } +++ +++ expectedPayload := (method == "POST" || method == "PUT") +++ +++ if expectedPayload && req.Header.Get("Content-Type") == "" { +++ req.Header.Set("Content-Type", "text/plain") +++ } +++ +++ if headers != nil { +++ for k, v := range headers { +++ req.Header[k] = v +++ } +++ } +++} +++ +++func encodeData(data interface{}) (*bytes.Buffer, error) { +++ params := bytes.NewBuffer(nil) +++ if data != nil { +++ if err := json.NewEncoder(params).Encode(data); err != nil { +++ return nil, err +++ } +++ } +++ return params, nil +++} diff --cc libnetwork/cmd/dnet/dnet_test.go index 00000000,00000000,00000000..b8466f1a new file mode 100644 --- /dev/null +++ b/libnetwork/cmd/dnet/dnet_test.go @@@@ -1,0 -1,0 -1,0 +1,132 @@@@ +++package main +++ +++import ( +++ "fmt" +++ "io/ioutil" +++ "os" +++ "testing" +++ "time" +++ +++ "github.com/docker/libnetwork/netutils" +++) +++ +++const dnetCommandName = "dnet" +++ +++var origStdOut = os.Stdout +++ +++func TestDnetDaemonCustom(t *testing.T) { +++ if !netutils.IsRunningInContainer() { +++ t.Skip("This test must run inside a container ") +++ } +++ customPort := 4567 +++ doneChan := make(chan bool) +++ go func() { +++ args := []string{dnetCommandName, "-d", fmt.Sprintf("-H=:%d", customPort)} +++ executeDnetCommand(t, args, true) +++ doneChan <- true +++ }() +++ +++ select { +++ case <-doneChan: +++ t.Fatal("dnet Daemon is not supposed to exit") +++ case <-time.After(3 * time.Second): +++ args := []string{dnetCommandName, "-d=false", fmt.Sprintf("-H=:%d", customPort), "-D", "network", "ls"} +++ executeDnetCommand(t, args, true) +++ } +++} +++ +++func TestDnetDaemonInvalidCustom(t *testing.T) { +++ if !netutils.IsRunningInContainer() { +++ t.Skip("This test must run inside a container ") +++ } +++ customPort := 4668 +++ doneChan := make(chan bool) +++ go func() { +++ args := []string{dnetCommandName, "-d=true", fmt.Sprintf("-H=:%d", customPort)} +++ executeDnetCommand(t, args, true) +++ doneChan <- true +++ }() +++ +++ select { +++ case <-doneChan: +++ t.Fatal("dnet Daemon is not supposed to exit") +++ case <-time.After(3 * time.Second): +++ args := []string{dnetCommandName, "-d=false", "-H=:6669", "-D", "network", "ls"} +++ executeDnetCommand(t, args, false) +++ } +++} +++ +++func TestDnetDaemonInvalidParams(t *testing.T) { +++ if !netutils.IsRunningInContainer() { +++ t.Skip("This test must run inside a container ") +++ } +++ args := []string{dnetCommandName, "-d=false", "-H=tcp:/127.0.0.1:8080"} +++ executeDnetCommand(t, args, false) +++ +++ args = []string{dnetCommandName, "-d=false", "-H=unix://var/run/dnet.sock"} +++ executeDnetCommand(t, args, false) +++ +++ args = []string{dnetCommandName, "-d=false", "-H=", "-l=invalid"} +++ executeDnetCommand(t, args, false) +++ +++ args = []string{dnetCommandName, "-d=false", "-H=", "-l=error", "invalid"} +++ executeDnetCommand(t, args, false) +++} +++ +++func TestDnetDefaultsWithFlags(t *testing.T) { +++ if !netutils.IsRunningInContainer() { +++ t.Skip("This test must run inside a container ") +++ } +++ doneChan := make(chan bool) +++ go func() { +++ args := []string{dnetCommandName, "-d=true", "-H=", "-l=error"} +++ executeDnetCommand(t, args, true) +++ doneChan <- true +++ }() +++ +++ select { +++ case <-doneChan: +++ t.Fatal("dnet Daemon is not supposed to exit") +++ case <-time.After(3 * time.Second): +++ args := []string{dnetCommandName, "-d=false", "network", "create", "-d=null", "test"} +++ executeDnetCommand(t, args, true) +++ +++ args = []string{dnetCommandName, "-d=false", "-D", "network", "ls"} +++ executeDnetCommand(t, args, true) +++ } +++} +++ +++func TestDnetMain(t *testing.T) { +++ if !netutils.IsRunningInContainer() { +++ t.Skip("This test must run inside a container ") +++ } +++ customPort := 4568 +++ doneChan := make(chan bool) +++ go func() { +++ args := []string{dnetCommandName, "-d=true", "-h=false", fmt.Sprintf("-H=:%d", customPort)} +++ os.Args = args +++ main() +++ doneChan <- true +++ }() +++ select { +++ case <-doneChan: +++ t.Fatal("dnet Daemon is not supposed to exit") +++ case <-time.After(2 * time.Second): +++ } +++} +++ +++func executeDnetCommand(t *testing.T, args []string, shouldSucced bool) { +++ _, w, _ := os.Pipe() +++ os.Stdout = w +++ +++ os.Args = args +++ err := dnetCommand(ioutil.Discard, ioutil.Discard) +++ if shouldSucced && err != nil { +++ os.Stdout = origStdOut +++ t.Fatalf("cli [%v] must succeed, but failed with an error : %v", args, err) +++ } else if !shouldSucced && err == nil { +++ os.Stdout = origStdOut +++ t.Fatalf("cli [%v] must fail, but succeeded with an error : %v", args, err) +++ } +++ os.Stdout = origStdOut +++} diff --cc libnetwork/cmd/dnet/flags.go index 00000000,00000000,00000000..27dfbd19 new file mode 100644 --- /dev/null +++ b/libnetwork/cmd/dnet/flags.go @@@@ -1,0 -1,0 -1,0 +1,51 @@@@ +++package main +++ +++import ( +++ "fmt" +++ "os" +++ +++ flag "github.com/docker/docker/pkg/mflag" +++) +++ +++type command struct { +++ name string +++ description string +++} +++ +++type byName []command +++ +++var ( +++ flDaemon = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode") +++ flHost = flag.String([]string{"H", "-host"}, "", "Daemon socket to connect to") +++ flLogLevel = flag.String([]string{"l", "-log-level"}, "info", "Set the logging level") +++ flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode") +++ flCfgFile = flag.String([]string{"c", "-cfg-file"}, "/etc/default/libnetwork.toml", "Configuration file") +++ flHelp = flag.Bool([]string{"h", "-help"}, false, "Print usage") +++ +++ dnetCommands = []command{ +++ {"network", "Network management commands"}, +++ {"service", "Service management commands"}, +++ } +++) +++ +++func init() { +++ flag.Usage = func() { +++ fmt.Fprint(os.Stdout, "Usage: dnet [OPTIONS] COMMAND [arg...]\n\nA self-sufficient runtime for container networking.\n\nOptions:\n") +++ +++ flag.CommandLine.SetOutput(os.Stdout) +++ flag.PrintDefaults() +++ +++ help := "\nCommands:\n" +++ +++ for _, cmd := range dnetCommands { +++ help += fmt.Sprintf(" %-10.10s%s\n", cmd.name, cmd.description) +++ } +++ +++ help += "\nRun 'dnet COMMAND --help' for more information on a command." +++ fmt.Fprintf(os.Stdout, "%s\n", help) +++ } +++} +++ +++func printUsage() { +++ fmt.Println("Usage: dnet COMMAND [arg...]") +++} diff --cc libnetwork/cmd/dnet/libnetwork.toml index 00000000,00000000,00000000..4e22516d new file mode 100755 --- /dev/null +++ b/libnetwork/cmd/dnet/libnetwork.toml @@@@ -1,0 -1,0 -1,0 +1,12 @@@@ +++title = "LibNetwork Configuration file" +++ +++[daemon] +++ debug = false +++[cluster] +++ discovery = "token://22aa23948f4f6b31230687689636959e" +++ Address = "1.1.1.1" +++[datastore] +++ embedded = false +++[datastore.client] +++ provider = "consul" +++ Address = "localhost:8500" diff --cc libnetwork/cmd/ovrouter/ovrouter.go index 00000000,00000000,00000000..a75b5506 new file mode 100644 --- /dev/null +++ b/libnetwork/cmd/ovrouter/ovrouter.go @@@@ -1,0 -1,0 -1,0 +1,149 @@@@ +++package main +++ +++import ( +++ "fmt" +++ "net" +++ "os" +++ "os/signal" +++ +++ "github.com/docker/docker/pkg/reexec" +++ "github.com/docker/libnetwork/driverapi" +++ "github.com/docker/libnetwork/drivers/overlay" +++ "github.com/docker/libnetwork/netlabel" +++ "github.com/docker/libnetwork/types" +++ "github.com/vishvananda/netlink" +++) +++ +++type router struct { +++ d driverapi.Driver +++} +++ +++type endpoint struct { +++ addr net.IPNet +++ mac net.HardwareAddr +++ name string +++ id int +++} +++ +++func (r *router) RegisterDriver(name string, driver driverapi.Driver, c driverapi.Capability) error { +++ r.d = driver +++ return nil +++} +++ +++func (ep *endpoint) Interfaces() []driverapi.InterfaceInfo { +++ return nil +++} +++ +++func (ep *endpoint) AddInterface(ID int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error { +++ ep.id = ID +++ ep.addr = ipv4 +++ ep.mac = mac +++ return nil +++} +++ +++func (ep *endpoint) InterfaceNames() []driverapi.InterfaceNameInfo { +++ return []driverapi.InterfaceNameInfo{ep} +++ +++} +++ +++func (ep *endpoint) SetNames(srcName, dstPrefix string) error { +++ ep.name = srcName +++ return nil +++} +++ +++func (ep *endpoint) ID() int { +++ return ep.id +++} +++ +++func (ep *endpoint) SetGateway(net.IP) error { +++ return nil +++} +++ +++func (ep *endpoint) SetGatewayIPv6(net.IP) error { +++ return nil +++} +++ +++func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int, +++ nextHop net.IP, interfaceID int) error { +++ return nil +++} +++ +++func (ep *endpoint) SetHostsPath(string) error { +++ return nil +++} +++ +++func (ep *endpoint) SetResolvConfPath(string) error { +++ return nil +++} +++ +++func main() { +++ if reexec.Init() { +++ return +++ } +++ +++ r := &router{} +++ if err := overlay.Init(r); err != nil { +++ fmt.Printf("Failed to initialize overlay driver: %v\n", err) +++ os.Exit(1) +++ } +++ +++ opt := make(map[string]interface{}) +++ if len(os.Args) > 1 { +++ opt[netlabel.OverlayBindInterface] = os.Args[1] +++ } +++ if len(os.Args) > 2 { +++ opt[netlabel.OverlayNeighborIP] = os.Args[2] +++ } +++ if len(os.Args) > 3 { +++ opt[netlabel.KVProvider] = os.Args[3] +++ } +++ if len(os.Args) > 4 { +++ opt[netlabel.KVProviderURL] = os.Args[4] +++ } +++ +++ r.d.Config(opt) +++ +++ if err := r.d.CreateNetwork(types.UUID("testnetwork"), +++ map[string]interface{}{}); err != nil { +++ fmt.Printf("Failed to create network in the driver: %v\n", err) +++ os.Exit(1) +++ } +++ +++ ep := &endpoint{} +++ if err := r.d.CreateEndpoint(types.UUID("testnetwork"), types.UUID("testep"), +++ ep, map[string]interface{}{}); err != nil { +++ fmt.Printf("Failed to create endpoint in the driver: %v\n", err) +++ os.Exit(1) +++ } +++ +++ if err := r.d.Join(types.UUID("testnetwork"), types.UUID("testep"), +++ "", ep, map[string]interface{}{}); err != nil { +++ fmt.Printf("Failed to join an endpoint in the driver: %v\n", err) +++ os.Exit(1) +++ } +++ +++ link, err := netlink.LinkByName(ep.name) +++ if err != nil { +++ fmt.Printf("Failed to find the container interface with name %s: %v\n", +++ ep.name, err) +++ os.Exit(1) +++ } +++ +++ ipAddr := &netlink.Addr{IPNet: &ep.addr, Label: ""} +++ if err := netlink.AddrAdd(link, ipAddr); err != nil { +++ fmt.Printf("Failed to add address to the interface: %v\n", err) +++ os.Exit(1) +++ } +++ +++ sigCh := make(chan os.Signal, 1) +++ signal.Notify(sigCh, os.Interrupt, os.Kill) +++ +++ for { +++ select { +++ case <-sigCh: +++ r.d.Leave(types.UUID("testnetwork"), types.UUID("testep")) +++ overlay.Fini(r.d) +++ os.Exit(0) +++ } +++ } +++} diff --cc libnetwork/cmd/readme_test/readme.go index 00000000,00000000,00000000..669b517f new file mode 100644 --- /dev/null +++ b/libnetwork/cmd/readme_test/readme.go @@@@ -1,0 -1,0 -1,0 +1,65 @@@@ +++package main +++ +++import ( +++ "fmt" +++ +++ "github.com/docker/libnetwork" +++ "github.com/docker/libnetwork/netlabel" +++ "github.com/docker/libnetwork/options" +++ "github.com/docker/libnetwork/types" +++) +++ +++func main() { +++ // Create a new controller instance +++ controller, err := libnetwork.New() +++ if err != nil { +++ return +++ } +++ +++ // Select and configure the network driver +++ networkType := "bridge" +++ +++ driverOptions := options.Generic{} +++ genericOption := make(map[string]interface{}) +++ genericOption[netlabel.GenericData] = driverOptions +++ err = controller.ConfigureNetworkDriver(networkType, genericOption) +++ if err != nil { +++ return +++ } +++ +++ // Create a network for containers to join. +++ // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can make of +++ network, err := controller.NewNetwork(networkType, "network1") +++ if err != nil { +++ return +++ } +++ +++ // For each new container: allocate IP and interfaces. The returned network +++ // settings will be used for container infos (inspect and such), as well as +++ // iptables rules for port publishing. This info is contained or accessible +++ // from the returned endpoint. +++ ep, err := network.CreateEndpoint("Endpoint1") +++ if err != nil { +++ return +++ } +++ +++ // A container can join the endpoint by providing the container ID to the join +++ // api. +++ // Join accepts Variadic arguments which will be made use of by libnetwork and Drivers +++ err = ep.Join("container1", +++ libnetwork.JoinOptionHostname("test"), +++ libnetwork.JoinOptionDomainname("docker.io")) +++ if err != nil { +++ return +++ } +++ +++ // libnetwork client can check the endpoint's operational data via the Info() API +++ epInfo, err := ep.DriverInfo() +++ mapData, ok := epInfo[netlabel.PortMap] +++ if ok { +++ portMapping, ok := mapData.([]types.PortBinding) +++ if ok { +++ fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping) +++ } +++ } +++} diff --cc libnetwork/cmd/test/libnetwork.toml index 00000000,00000000,00000000..4e22516d new file mode 100644 --- /dev/null +++ b/libnetwork/cmd/test/libnetwork.toml @@@@ -1,0 -1,0 -1,0 +1,12 @@@@ +++title = "LibNetwork Configuration file" +++ +++[daemon] +++ debug = false +++[cluster] +++ discovery = "token://22aa23948f4f6b31230687689636959e" +++ Address = "1.1.1.1" +++[datastore] +++ embedded = false +++[datastore.client] +++ provider = "consul" +++ Address = "localhost:8500" diff --cc libnetwork/cmd/test/main.go index 00000000,00000000,00000000..957a0cda new file mode 100644 --- /dev/null +++ b/libnetwork/cmd/test/main.go @@@@ -1,0 -1,0 -1,0 +1,49 @@@@ +++package main +++ +++import ( +++ "fmt" +++ "net" +++ "time" +++ +++ log "github.com/Sirupsen/logrus" +++ +++ "github.com/docker/libnetwork" +++ "github.com/docker/libnetwork/options" +++) +++ +++func main() { +++ log.SetLevel(log.DebugLevel) +++ controller, err := libnetwork.New() +++ if err != nil { +++ log.Fatal(err) +++ } +++ +++ netType := "null" +++ ip, net, _ := net.ParseCIDR("192.168.100.1/24") +++ net.IP = ip +++ options := options.Generic{"AddressIPv4": net} +++ +++ err = controller.ConfigureNetworkDriver(netType, options) +++ for i := 0; i < 10; i++ { +++ netw, err := controller.NewNetwork(netType, fmt.Sprintf("Gordon-%d", i)) +++ if err != nil { +++ if _, ok := err.(libnetwork.NetworkNameError); !ok { +++ log.Fatal(err) +++ } +++ } else { +++ fmt.Println("Network Created Successfully :", netw) +++ } +++ netw, _ = controller.NetworkByName(fmt.Sprintf("Gordon-%d", i)) +++ _, err = netw.CreateEndpoint(fmt.Sprintf("Gordon-Ep-%d", i), nil) +++ if err != nil { +++ log.Fatalf("Error creating endpoint 1 %v", err) +++ } +++ +++ _, err = netw.CreateEndpoint(fmt.Sprintf("Gordon-Ep2-%d", i), nil) +++ if err != nil { +++ log.Fatalf("Error creating endpoint 2 %v", err) +++ } +++ +++ time.Sleep(2 * time.Second) +++ } +++} diff --cc libnetwork/config/config.go index 00000000,00000000,00000000..bb93d981 new file mode 100644 --- /dev/null +++ b/libnetwork/config/config.go @@@@ -1,0 -1,0 -1,0 +1,116 @@@@ +++package config +++ +++import ( +++ "strings" +++ +++ "github.com/BurntSushi/toml" +++ log "github.com/Sirupsen/logrus" +++ "github.com/docker/libnetwork/netlabel" +++) +++ +++// Config encapsulates configurations of various Libnetwork components +++type Config struct { +++ Daemon DaemonCfg +++ Cluster ClusterCfg +++ Datastore DatastoreCfg +++} +++ +++// DaemonCfg represents libnetwork core configuration +++type DaemonCfg struct { +++ Debug bool +++ DefaultNetwork string +++ DefaultDriver string +++ Labels []string +++} +++ +++// ClusterCfg represents cluster configuration +++type ClusterCfg struct { +++ Discovery string +++ Address string +++ Heartbeat uint64 +++} +++ +++// DatastoreCfg represents Datastore configuration. +++type DatastoreCfg struct { +++ Embedded bool +++ Client DatastoreClientCfg +++} +++ +++// DatastoreClientCfg represents Datastore Client-only mode configuration +++type DatastoreClientCfg struct { +++ Provider string +++ Address string +++} +++ +++// ParseConfig parses the libnetwork configuration file +++func ParseConfig(tomlCfgFile string) (*Config, error) { +++ var cfg Config +++ if _, err := toml.DecodeFile(tomlCfgFile, &cfg); err != nil { +++ return nil, err +++ } +++ return &cfg, nil +++} +++ +++// Option is a option setter function type used to pass varios configurations +++// to the controller +++type Option func(c *Config) +++ +++// OptionDefaultNetwork function returns an option setter for a default network +++func OptionDefaultNetwork(dn string) Option { +++ return func(c *Config) { +++ log.Infof("Option DefaultNetwork: %s", dn) +++ c.Daemon.DefaultNetwork = strings.TrimSpace(dn) +++ } +++} +++ +++// OptionDefaultDriver function returns an option setter for default driver +++func OptionDefaultDriver(dd string) Option { +++ return func(c *Config) { +++ log.Infof("Option DefaultDriver: %s", dd) +++ c.Daemon.DefaultDriver = strings.TrimSpace(dd) +++ } +++} +++ +++// OptionLabels function returns an option setter for labels +++func OptionLabels(labels []string) Option { +++ return func(c *Config) { +++ for _, label := range labels { +++ if strings.HasPrefix(label, netlabel.Prefix) { +++ c.Daemon.Labels = append(c.Daemon.Labels, label) +++ } +++ } +++ } +++} +++ +++// OptionKVProvider function returns an option setter for kvstore provider +++func OptionKVProvider(provider string) Option { +++ return func(c *Config) { +++ log.Infof("Option OptionKVProvider: %s", provider) +++ c.Datastore.Client.Provider = strings.TrimSpace(provider) +++ } +++} +++ +++// OptionKVProviderURL function returns an option setter for kvstore url +++func OptionKVProviderURL(url string) Option { +++ return func(c *Config) { +++ log.Infof("Option OptionKVProviderURL: %s", url) +++ c.Datastore.Client.Address = strings.TrimSpace(url) +++ } +++} +++ +++// ProcessOptions processes options and stores it in config +++func (c *Config) ProcessOptions(options ...Option) { +++ for _, opt := range options { +++ if opt != nil { +++ opt(c) +++ } +++ } +++} +++ +++// IsValidName validates configuration objects supported by libnetwork +++func IsValidName(name string) bool { +++ if name == "" || strings.Contains(name, ".") { +++ return false +++ } +++ return true +++} diff --cc libnetwork/config/config_test.go index 00000000,00000000,00000000..cc8a911c new file mode 100644 --- /dev/null +++ b/libnetwork/config/config_test.go @@@@ -1,0 -1,0 -1,0 +1,55 @@@@ +++package config +++ +++import ( +++ "strings" +++ "testing" +++ +++ "github.com/docker/libnetwork/netlabel" +++ _ "github.com/docker/libnetwork/netutils" +++) +++ +++func TestInvalidConfig(t *testing.T) { +++ _, err := ParseConfig("invalid.toml") +++ if err == nil { +++ t.Fatal("Invalid Configuration file must fail") +++ } +++} +++ +++func TestConfig(t *testing.T) { +++ _, err := ParseConfig("libnetwork.toml") +++ if err != nil { +++ t.Fatal("Error parsing a valid configuration file :", err) +++ } +++} +++ +++func TestOptionsLabels(t *testing.T) { +++ c := &Config{} +++ l := []string{ +++ "com.docker.network.key1=value1", +++ "com.docker.storage.key1=value1", +++ "com.docker.network.driver.key1=value1", +++ "com.docker.network.driver.key2=value2", +++ } +++ f := OptionLabels(l) +++ f(c) +++ if len(c.Daemon.Labels) != 3 { +++ t.Fatalf("Expecting 3 labels, seen %d", len(c.Daemon.Labels)) +++ } +++ for _, l := range c.Daemon.Labels { +++ if !strings.HasPrefix(l, netlabel.Prefix) { +++ t.Fatalf("config must accept only libnetwork labels. Not : %s", l) +++ } +++ } +++} +++ +++func TestValidName(t *testing.T) { +++ if !IsValidName("test") { +++ t.Fatal("Name validation fails for a name that must be accepted") +++ } +++ if IsValidName("") { +++ t.Fatal("Name validation succeeds for a case when it is expected to fail") +++ } +++ if IsValidName("name.with.dots") { +++ t.Fatal("Name validation succeeds for a case when it is expected to fail") +++ } +++} diff --cc libnetwork/config/libnetwork.toml index 00000000,00000000,00000000..93a2ff47 new file mode 100644 --- /dev/null +++ b/libnetwork/config/libnetwork.toml @@@@ -1,0 -1,0 -1,0 +1,12 @@@@ +++title = "LibNetwork Configuration file" +++ +++[daemon] +++ debug = false +++[cluster] +++ discovery = "token://swarm-discovery-token" +++ Address = "Cluster-wide reachable Host IP" +++[datastore] +++ embedded = false +++[datastore.client] +++ provider = "consul" +++ Address = "localhost:8500" diff --cc libnetwork/controller.go index 00000000,00000000,00000000..02a9f7eb new file mode 100644 --- /dev/null +++ b/libnetwork/controller.go @@@@ -1,0 -1,0 -1,0 +1,399 @@@@ +++/* +++Package libnetwork provides the basic functionality and extension points to +++create network namespaces and allocate interfaces for containers to use. +++ +++ // Create a new controller instance +++ controller, _err := libnetwork.New(nil) +++ +++ // Select and configure the network driver +++ networkType := "bridge" +++ +++ driverOptions := options.Generic{} +++ genericOption := make(map[string]interface{}) +++ genericOption[netlabel.GenericData] = driverOptions +++ err := controller.ConfigureNetworkDriver(networkType, genericOption) +++ if err != nil { +++ return +++ } +++ +++ // Create a network for containers to join. +++ // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can make of +++ network, err := controller.NewNetwork(networkType, "network1") +++ if err != nil { +++ return +++ } +++ +++ // For each new container: allocate IP and interfaces. The returned network +++ // settings will be used for container infos (inspect and such), as well as +++ // iptables rules for port publishing. This info is contained or accessible +++ // from the returned endpoint. +++ ep, err := network.CreateEndpoint("Endpoint1") +++ if err != nil { +++ return +++ } +++ +++ // A container can join the endpoint by providing the container ID to the join +++ // api. +++ // Join accepts Variadic arguments which will be made use of by libnetwork and Drivers +++ err = ep.Join("container1", +++ libnetwork.JoinOptionHostname("test"), +++ libnetwork.JoinOptionDomainname("docker.io")) +++ if err != nil { +++ return +++ } +++*/ +++package libnetwork +++ +++import ( +++ "fmt" +++ "net" +++ "strings" +++ "sync" +++ +++ log "github.com/Sirupsen/logrus" +++ "github.com/docker/docker/pkg/plugins" +++ "github.com/docker/docker/pkg/stringid" +++ "github.com/docker/libnetwork/config" +++ "github.com/docker/libnetwork/datastore" +++ "github.com/docker/libnetwork/driverapi" +++ "github.com/docker/libnetwork/hostdiscovery" +++ "github.com/docker/libnetwork/netlabel" +++ "github.com/docker/libnetwork/sandbox" +++ "github.com/docker/libnetwork/types" +++) +++ +++// NetworkController provides the interface for controller instance which manages +++// networks. +++type NetworkController interface { +++ // ConfigureNetworkDriver applies the passed options to the driver instance for the specified network type +++ ConfigureNetworkDriver(networkType string, options map[string]interface{}) error +++ +++ // Config method returns the bootup configuration for the controller +++ Config() config.Config +++ +++ // Create a new network. The options parameter carries network specific options. +++ // Labels support will be added in the near future. +++ NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) +++ +++ // Networks returns the list of Network(s) managed by this controller. +++ Networks() []Network +++ +++ // WalkNetworks uses the provided function to walk the Network(s) managed by this controller. +++ WalkNetworks(walker NetworkWalker) +++ +++ // NetworkByName returns the Network which has the passed name. If not found, the error ErrNoSuchNetwork is returned. +++ NetworkByName(name string) (Network, error) +++ +++ // NetworkByID returns the Network which has the passed id. If not found, the error ErrNoSuchNetwork is returned. +++ NetworkByID(id string) (Network, error) +++ +++ // LeaveAll accepts a container id and attempts to leave all endpoints that the container has joined +++ LeaveAll(id string) error +++ +++ // GC triggers immediate garbage collection of resources which are garbage collected. +++ GC() +++} +++ +++// NetworkWalker is a client provided function which will be used to walk the Networks. +++// When the function returns true, the walk will stop. +++type NetworkWalker func(nw Network) bool +++ +++type driverData struct { +++ driver driverapi.Driver +++ capability driverapi.Capability +++} +++ +++type driverTable map[string]*driverData +++type networkTable map[types.UUID]*network +++type endpointTable map[types.UUID]*endpoint +++type sandboxTable map[string]*sandboxData +++ +++type controller struct { +++ networks networkTable +++ drivers driverTable +++ sandboxes sandboxTable +++ cfg *config.Config +++ store datastore.DataStore +++ sync.Mutex +++} +++ +++// New creates a new instance of network controller. +++func New(cfgOptions ...config.Option) (NetworkController, error) { +++ var cfg *config.Config +++ if len(cfgOptions) > 0 { +++ cfg = &config.Config{} +++ cfg.ProcessOptions(cfgOptions...) +++ } +++ c := &controller{ +++ cfg: cfg, +++ networks: networkTable{}, +++ sandboxes: sandboxTable{}, +++ drivers: driverTable{}} +++ if err := initDrivers(c); err != nil { +++ return nil, err +++ } +++ +++ if cfg != nil { +++ if err := c.initDataStore(); err != nil { +++ // Failing to initalize datastore is a bad situation to be in. +++ // But it cannot fail creating the Controller +++ log.Debugf("Failed to Initialize Datastore due to %v. Operating in non-clustered mode", err) +++ } +++ if err := c.initDiscovery(); err != nil { +++ // Failing to initalize discovery is a bad situation to be in. +++ // But it cannot fail creating the Controller +++ log.Debugf("Failed to Initialize Discovery : %v", err) +++ } +++ } +++ +++ return c, nil +++} +++ +++func (c *controller) validateHostDiscoveryConfig() bool { +++ if c.cfg == nil || c.cfg.Cluster.Discovery == "" || c.cfg.Cluster.Address == "" { +++ return false +++ } +++ return true +++} +++ +++func (c *controller) initDiscovery() error { +++ if c.cfg == nil { +++ return fmt.Errorf("discovery initialization requires a valid configuration") +++ } +++ +++ hostDiscovery := hostdiscovery.NewHostDiscovery() +++ return hostDiscovery.StartDiscovery(&c.cfg.Cluster, c.hostJoinCallback, c.hostLeaveCallback) +++} +++ +++func (c *controller) hostJoinCallback(hosts []net.IP) { +++} +++ +++func (c *controller) hostLeaveCallback(hosts []net.IP) { +++} +++ +++func (c *controller) Config() config.Config { +++ c.Lock() +++ defer c.Unlock() +++ if c.cfg == nil { +++ return config.Config{} +++ } +++ return *c.cfg +++} +++ +++func (c *controller) ConfigureNetworkDriver(networkType string, options map[string]interface{}) error { +++ c.Lock() +++ dd, ok := c.drivers[networkType] +++ c.Unlock() +++ if !ok { +++ return NetworkTypeError(networkType) +++ } +++ return dd.driver.Config(options) +++} +++ +++func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver, capability driverapi.Capability) error { +++ c.Lock() +++ if !config.IsValidName(networkType) { +++ c.Unlock() +++ return ErrInvalidName(networkType) +++ } +++ if _, ok := c.drivers[networkType]; ok { +++ c.Unlock() +++ return driverapi.ErrActiveRegistration(networkType) +++ } +++ c.drivers[networkType] = &driverData{driver, capability} +++ +++ if c.cfg == nil { +++ c.Unlock() +++ return nil +++ } +++ +++ opt := make(map[string]interface{}) +++ for _, label := range c.cfg.Daemon.Labels { +++ if strings.HasPrefix(label, netlabel.DriverPrefix+"."+networkType) { +++ opt[netlabel.Key(label)] = netlabel.Value(label) +++ } +++ } +++ +++ if capability.Scope == driverapi.GlobalScope && c.validateDatastoreConfig() { +++ opt[netlabel.KVProvider] = c.cfg.Datastore.Client.Provider +++ opt[netlabel.KVProviderURL] = c.cfg.Datastore.Client.Address +++ } +++ +++ c.Unlock() +++ +++ if len(opt) != 0 { +++ if err := driver.Config(opt); err != nil { +++ return err +++ } +++ } +++ +++ return nil +++} +++ +++// NewNetwork creates a new network of the specified network type. The options +++// are network specific and modeled in a generic way. +++func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) { +++ if !config.IsValidName(name) { +++ return nil, ErrInvalidName(name) +++ } +++ // Check if a network already exists with the specified network name +++ c.Lock() +++ for _, n := range c.networks { +++ if n.name == name { +++ c.Unlock() +++ return nil, NetworkNameError(name) +++ } +++ } +++ c.Unlock() +++ +++ // Construct the network object +++ network := &network{ +++ name: name, +++ networkType: networkType, +++ id: types.UUID(stringid.GenerateRandomID()), +++ ctrlr: c, +++ endpoints: endpointTable{}, +++ } +++ +++ network.processOptions(options...) +++ +++ if err := c.addNetwork(network); err != nil { +++ return nil, err +++ } +++ +++ if err := c.updateNetworkToStore(network); err != nil { +++ log.Warnf("couldnt create network %s: %v", network.name, err) +++ if e := network.Delete(); e != nil { +++ log.Warnf("couldnt cleanup network %s: %v", network.name, err) +++ } +++ return nil, err +++ } +++ +++ return network, nil +++} +++ +++func (c *controller) addNetwork(n *network) error { +++ +++ c.Lock() +++ // Check if a driver for the specified network type is available +++ dd, ok := c.drivers[n.networkType] +++ c.Unlock() +++ +++ if !ok { +++ var err error +++ dd, err = c.loadDriver(n.networkType) +++ if err != nil { +++ return err +++ } +++ } +++ +++ n.Lock() +++ n.svcRecords = svcMap{} +++ n.driver = dd.driver +++ d := n.driver +++ n.Unlock() +++ +++ // Create the network +++ if err := d.CreateNetwork(n.id, n.generic); err != nil { +++ return err +++ } +++ if err := n.watchEndpoints(); err != nil { +++ return err +++ } +++ c.Lock() +++ c.networks[n.id] = n +++ c.Unlock() +++ +++ return nil +++} +++ +++func (c *controller) Networks() []Network { +++ c.Lock() +++ defer c.Unlock() +++ +++ list := make([]Network, 0, len(c.networks)) +++ for _, n := range c.networks { +++ list = append(list, n) +++ } +++ +++ return list +++} +++ +++func (c *controller) WalkNetworks(walker NetworkWalker) { +++ for _, n := range c.Networks() { +++ if walker(n) { +++ return +++ } +++ } +++} +++ +++func (c *controller) NetworkByName(name string) (Network, error) { +++ if name == "" { +++ return nil, ErrInvalidName(name) +++ } +++ var n Network +++ +++ s := func(current Network) bool { +++ if current.Name() == name { +++ n = current +++ return true +++ } +++ return false +++ } +++ +++ c.WalkNetworks(s) +++ +++ if n == nil { +++ return nil, ErrNoSuchNetwork(name) +++ } +++ +++ return n, nil +++} +++ +++func (c *controller) NetworkByID(id string) (Network, error) { +++ if id == "" { +++ return nil, ErrInvalidID(id) +++ } +++ c.Lock() +++ defer c.Unlock() +++ if n, ok := c.networks[types.UUID(id)]; ok { +++ return n, nil +++ } +++ return nil, ErrNoSuchNetwork(id) +++} +++ +++func (c *controller) loadDriver(networkType string) (*driverData, error) { +++ // Plugins pkg performs lazy loading of plugins that acts as remote drivers. +++ // As per the design, this Get call will result in remote driver discovery if there is a corresponding plugin available. +++ _, err := plugins.Get(networkType, driverapi.NetworkPluginEndpointType) +++ if err != nil { +++ if err == plugins.ErrNotFound { +++ return nil, types.NotFoundErrorf(err.Error()) +++ } +++ return nil, err +++ } +++ c.Lock() +++ defer c.Unlock() +++ dd, ok := c.drivers[networkType] +++ if !ok { +++ return nil, ErrInvalidNetworkDriver(networkType) +++ } +++ return dd, nil +++} +++ +++func (c *controller) isDriverGlobalScoped(networkType string) (bool, error) { +++ c.Lock() +++ dd, ok := c.drivers[networkType] +++ c.Unlock() +++ if !ok { +++ return false, types.NotFoundErrorf("driver not found for %s", networkType) +++ } +++ if dd.capability.Scope == driverapi.GlobalScope { +++ return true, nil +++ } +++ return false, nil +++} +++ +++func (c *controller) GC() { +++ sandbox.GC() +++} diff --cc libnetwork/datastore/datastore.go index 00000000,00000000,00000000..8a195aa9 new file mode 100644 --- /dev/null +++ b/libnetwork/datastore/datastore.go @@@@ -1,0 -1,0 -1,0 +1,194 @@@@ +++package datastore +++ +++import ( +++ "reflect" +++ "strings" +++ +++ "github.com/docker/libkv" +++ "github.com/docker/libkv/store" +++ "github.com/docker/libnetwork/config" +++ "github.com/docker/libnetwork/types" +++) +++ +++//DataStore exported +++type DataStore interface { +++ // GetObject gets data from datastore and unmarshals to the specified object +++ GetObject(key string, o KV) error +++ // PutObject adds a new Record based on an object into the datastore +++ PutObject(kvObject KV) error +++ // PutObjectAtomic provides an atomic add and update operation for a Record +++ PutObjectAtomic(kvObject KV) error +++ // DeleteObject deletes a record +++ DeleteObject(kvObject KV) error +++ // DeleteObjectAtomic performs an atomic delete operation +++ DeleteObjectAtomic(kvObject KV) error +++ // DeleteTree deletes a record +++ DeleteTree(kvObject KV) error +++ // KVStore returns access to the KV Store +++ KVStore() store.Store +++} +++ +++// ErrKeyModified is raised for an atomic update when the update is working on a stale state +++var ( +++ ErrKeyModified = store.ErrKeyModified +++ ErrKeyNotFound = store.ErrKeyNotFound +++) +++ +++type datastore struct { +++ store store.Store +++} +++ +++//KV Key Value interface used by objects to be part of the DataStore +++type KV interface { +++ // Key method lets an object to provide the Key to be used in KV Store +++ Key() []string +++ // KeyPrefix method lets an object to return immediate parent key that can be used for tree walk +++ KeyPrefix() []string +++ // Value method lets an object to marshal its content to be stored in the KV store +++ Value() []byte +++ // SetValue is used by the datastore to set the object's value when loaded from the data store. +++ SetValue([]byte) error +++ // Index method returns the latest DB Index as seen by the object +++ Index() uint64 +++ // SetIndex method allows the datastore to store the latest DB Index into the object +++ SetIndex(uint64) +++ // True if the object exists in the datastore, false if it hasn't been stored yet. +++ // When SetIndex() is called, the object has been stored. +++ Exists() bool +++} +++ +++const ( +++ // NetworkKeyPrefix is the prefix for network key in the kv store +++ NetworkKeyPrefix = "network" +++ // EndpointKeyPrefix is the prefix for endpoint key in the kv store +++ EndpointKeyPrefix = "endpoint" +++) +++ +++var rootChain = []string{"docker", "libnetwork"} +++ +++//Key provides convenient method to create a Key +++func Key(key ...string) string { +++ keychain := append(rootChain, key...) +++ str := strings.Join(keychain, "/") +++ return str + "/" +++} +++ +++//ParseKey provides convenient method to unpack the key to complement the Key function +++func ParseKey(key string) ([]string, error) { +++ chain := strings.Split(strings.Trim(key, "/"), "/") +++ +++ // The key must atleast be equal to the rootChain in order to be considered as valid +++ if len(chain) <= len(rootChain) || !reflect.DeepEqual(chain[0:len(rootChain)], rootChain) { +++ return nil, types.BadRequestErrorf("invalid Key : %s", key) +++ } +++ return chain[len(rootChain):], nil +++} +++ +++// newClient used to connect to KV Store +++func newClient(kv string, addrs string) (DataStore, error) { +++ store, err := libkv.NewStore(store.Backend(kv), []string{addrs}, &store.Config{}) +++ if err != nil { +++ return nil, err +++ } +++ ds := &datastore{store: store} +++ return ds, nil +++} +++ +++// NewDataStore creates a new instance of LibKV data store +++func NewDataStore(cfg *config.DatastoreCfg) (DataStore, error) { +++ if cfg == nil { +++ return nil, types.BadRequestErrorf("invalid configuration passed to datastore") +++ } +++ // TODO : cfg.Embedded case +++ return newClient(cfg.Client.Provider, cfg.Client.Address) +++} +++ +++// NewCustomDataStore can be used by clients to plugin cusom datatore that adhers to store.Store +++func NewCustomDataStore(customStore store.Store) DataStore { +++ return &datastore{store: customStore} +++} +++ +++func (ds *datastore) KVStore() store.Store { +++ return ds.store +++} +++ +++// PutObjectAtomic adds a new Record based on an object into the datastore +++func (ds *datastore) PutObjectAtomic(kvObject KV) error { +++ if kvObject == nil { +++ return types.BadRequestErrorf("invalid KV Object : nil") +++ } +++ kvObjValue := kvObject.Value() +++ +++ if kvObjValue == nil { +++ return types.BadRequestErrorf("invalid KV Object with a nil Value for key %s", Key(kvObject.Key()...)) +++ } +++ +++ var previous *store.KVPair +++ if kvObject.Exists() { +++ previous = &store.KVPair{Key: Key(kvObject.Key()...), LastIndex: kvObject.Index()} +++ } else { +++ previous = nil +++ } +++ _, pair, err := ds.store.AtomicPut(Key(kvObject.Key()...), kvObjValue, previous, nil) +++ if err != nil { +++ return err +++ } +++ +++ kvObject.SetIndex(pair.LastIndex) +++ return nil +++} +++ +++// PutObject adds a new Record based on an object into the datastore +++func (ds *datastore) PutObject(kvObject KV) error { +++ if kvObject == nil { +++ return types.BadRequestErrorf("invalid KV Object : nil") +++ } +++ return ds.putObjectWithKey(kvObject, kvObject.Key()...) +++} +++ +++func (ds *datastore) putObjectWithKey(kvObject KV, key ...string) error { +++ kvObjValue := kvObject.Value() +++ +++ if kvObjValue == nil { +++ return types.BadRequestErrorf("invalid KV Object with a nil Value for key %s", Key(kvObject.Key()...)) +++ } +++ return ds.store.Put(Key(key...), kvObjValue, nil) +++} +++ +++// GetObject returns a record matching the key +++func (ds *datastore) GetObject(key string, o KV) error { +++ kvPair, err := ds.store.Get(key) +++ if err != nil { +++ return err +++ } +++ err = o.SetValue(kvPair.Value) +++ if err != nil { +++ return err +++ } +++ +++ // Make sure the object has a correct view of the DB index in case we need to modify it +++ // and update the DB. +++ o.SetIndex(kvPair.LastIndex) +++ return nil +++} +++ +++// DeleteObject unconditionally deletes a record from the store +++func (ds *datastore) DeleteObject(kvObject KV) error { +++ return ds.store.Delete(Key(kvObject.Key()...)) +++} +++ +++// DeleteObjectAtomic performs atomic delete on a record +++func (ds *datastore) DeleteObjectAtomic(kvObject KV) error { +++ if kvObject == nil { +++ return types.BadRequestErrorf("invalid KV Object : nil") +++ } +++ +++ previous := &store.KVPair{Key: Key(kvObject.Key()...), LastIndex: kvObject.Index()} +++ _, err := ds.store.AtomicDelete(Key(kvObject.Key()...), previous) +++ return err +++} +++ +++// DeleteTree unconditionally deletes a record from the store +++func (ds *datastore) DeleteTree(kvObject KV) error { +++ return ds.store.DeleteTree(Key(kvObject.KeyPrefix()...)) +++} diff --cc libnetwork/datastore/datastore_test.go index 00000000,00000000,00000000..397842db new file mode 100644 --- /dev/null +++ b/libnetwork/datastore/datastore_test.go @@@@ -1,0 -1,0 -1,0 +1,241 @@@@ +++package datastore +++ +++import ( +++ "encoding/json" +++ "reflect" +++ "testing" +++ +++ "github.com/docker/libnetwork/config" +++ _ "github.com/docker/libnetwork/netutils" +++ "github.com/docker/libnetwork/options" +++ "github.com/stretchr/testify/assert" +++) +++ +++var dummyKey = "dummy" +++ +++// NewCustomDataStore can be used by other Tests in order to use custom datastore +++func NewTestDataStore() DataStore { +++ return &datastore{store: NewMockStore()} +++} +++ +++func TestKey(t *testing.T) { +++ eKey := []string{"hello", "world"} +++ sKey := Key(eKey...) +++ if sKey != "docker/libnetwork/hello/world/" { +++ t.Fatalf("unexpected key : %s", sKey) +++ } +++} +++ +++func TestParseKey(t *testing.T) { +++ keySlice, err := ParseKey("/docker/libnetwork/hello/world/") +++ if err != nil { +++ t.Fatal(err) +++ } +++ eKey := []string{"hello", "world"} +++ if len(keySlice) < 2 || !reflect.DeepEqual(eKey, keySlice) { +++ t.Fatalf("unexpected unkey : %s", keySlice) +++ } +++} +++ +++func TestInvalidDataStore(t *testing.T) { +++ config := &config.DatastoreCfg{} +++ config.Embedded = false +++ config.Client.Provider = "invalid" +++ config.Client.Address = "localhost:8500" +++ _, err := NewDataStore(config) +++ if err == nil { +++ t.Fatal("Invalid Datastore connection configuration must result in a failure") +++ } +++} +++ +++func TestKVObjectFlatKey(t *testing.T) { +++ store := NewTestDataStore() +++ expected := dummyKVObject("1000", true) +++ err := store.PutObject(expected) +++ if err != nil { +++ t.Fatal(err) +++ } +++ keychain := []string{dummyKey, "1000"} +++ data, err := store.KVStore().Get(Key(keychain...)) +++ if err != nil { +++ t.Fatal(err) +++ } +++ var n dummyObject +++ json.Unmarshal(data.Value, &n) +++ if n.Name != expected.Name { +++ t.Fatalf("Dummy object doesn't match the expected object") +++ } +++} +++ +++func TestAtomicKVObjectFlatKey(t *testing.T) { +++ store := NewTestDataStore() +++ expected := dummyKVObject("1111", true) +++ assert.False(t, expected.Exists()) +++ err := store.PutObjectAtomic(expected) +++ if err != nil { +++ t.Fatal(err) +++ } +++ assert.True(t, expected.Exists()) +++ +++ // PutObjectAtomic automatically sets the Index again. Hence the following must pass. +++ +++ err = store.PutObjectAtomic(expected) +++ if err != nil { +++ t.Fatal("Atomic update should succeed.") +++ } +++ +++ // Get the latest index and try PutObjectAtomic again for the same Key +++ // This must succeed as well +++ data, err := store.KVStore().Get(Key(expected.Key()...)) +++ if err != nil { +++ t.Fatal(err) +++ } +++ n := dummyObject{} +++ json.Unmarshal(data.Value, &n) +++ n.ID = "1111" +++ n.SetIndex(data.LastIndex) +++ n.ReturnValue = true +++ err = store.PutObjectAtomic(&n) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++ // Get the Object using GetObject, then set again. +++ newObj := dummyObject{} +++ err = store.GetObject(Key(expected.Key()...), &newObj) +++ assert.True(t, newObj.Exists()) +++ err = store.PutObjectAtomic(&n) +++ if err != nil { +++ t.Fatal(err) +++ } +++ +++} +++ +++// dummy data used to test the datastore +++type dummyObject struct { +++ Name string `kv:"leaf"` +++ NetworkType string `kv:"leaf"` +++ EnableIPv6 bool `kv:"leaf"` +++ Rec *recStruct `kv:"recursive"` +++ Dict map[string]*recStruct `kv:"iterative"` +++ Generic options.Generic `kv:"iterative"` +++ ID string +++ DBIndex uint64 +++ DBExists bool +++ ReturnValue bool +++} +++ +++func (n *dummyObject) Key() []string { +++ return []string{dummyKey, n.ID} +++} +++ +++func (n *dummyObject) KeyPrefix() []string { +++ return []string{dummyKey} +++} +++ +++func (n *dummyObject) Value() []byte { +++ if !n.ReturnValue { +++ return nil +++ } +++ +++ b, err := json.Marshal(n) +++ if err != nil { +++ return nil +++ } +++ return b +++} +++ +++func (n *dummyObject) SetValue(value []byte) error { +++ return json.Unmarshal(value, n) +++} +++ +++func (n *dummyObject) Index() uint64 { +++ return n.DBIndex +++} +++ +++func (n *dummyObject) SetIndex(index uint64) { +++ n.DBIndex = index +++ n.DBExists = true +++} +++ +++func (n *dummyObject) Exists() bool { +++ return n.DBExists +++} +++ +++func (n *dummyObject) MarshalJSON() ([]byte, error) { +++ netMap := make(map[string]interface{}) +++ netMap["name"] = n.Name +++ netMap["networkType"] = n.NetworkType +++ netMap["enableIPv6"] = n.EnableIPv6 +++ netMap["generic"] = n.Generic +++ return json.Marshal(netMap) +++} +++ +++func (n *dummyObject) UnmarshalJSON(b []byte) (err error) { +++ var netMap map[string]interface{} +++ if err := json.Unmarshal(b, &netMap); err != nil { +++ return err +++ } +++ n.Name = netMap["name"].(string) +++ n.NetworkType = netMap["networkType"].(string) +++ n.EnableIPv6 = netMap["enableIPv6"].(bool) +++ n.Generic = netMap["generic"].(map[string]interface{}) +++ return nil +++} +++ +++// dummy structure to test "recursive" cases +++type recStruct struct { +++ Name string `kv:"leaf"` +++ Field1 int `kv:"leaf"` +++ Dict map[string]string `kv:"iterative"` +++ DBIndex uint64 +++ DBExists bool +++} +++ +++func (r *recStruct) Key() []string { +++ return []string{"recStruct"} +++} +++func (r *recStruct) Value() []byte { +++ b, err := json.Marshal(r) +++ if err != nil { +++ return nil +++ } +++ return b +++} +++ +++func (r *recStruct) SetValue(value []byte) error { +++ return json.Unmarshal(value, r) +++} +++ +++func (r *recStruct) Index() uint64 { +++ return r.DBIndex +++} +++ +++func (r *recStruct) SetIndex(index uint64) { +++ r.DBIndex = index +++ r.DBExists = true +++} +++ +++func (r *recStruct) Exists() bool { +++ return r.DBExists +++} +++ +++func dummyKVObject(id string, retValue bool) *dummyObject { +++ cDict := make(map[string]string) +++ cDict["foo"] = "bar" +++ cDict["hello"] = "world" +++ n := dummyObject{ +++ Name: "testNw", +++ NetworkType: "bridge", +++ EnableIPv6: true, +++ Rec: &recStruct{"gen", 5, cDict, 0, false}, +++ ID: id, +++ DBIndex: 0, +++ ReturnValue: retValue, +++ DBExists: false} +++ generic := make(map[string]interface{}) +++ generic["label1"] = &recStruct{"value1", 1, cDict, 0, false} +++ generic["label2"] = "subnet=10.1.1.0/16" +++ n.Generic = generic +++ return &n +++} diff --cc libnetwork/datastore/mock_store.go index 00000000,00000000,00000000..0817339b new file mode 100644 --- /dev/null +++ b/libnetwork/datastore/mock_store.go @@@@ -1,0 -1,0 -1,0 +1,129 @@@@ +++package datastore +++ +++import ( +++ "errors" +++ +++ "github.com/docker/libkv/store" +++ "github.com/docker/libnetwork/types" +++) +++ +++var ( +++ // ErrNotImplmented exported +++ ErrNotImplmented = errors.New("Functionality not implemented") +++) +++ +++// MockData exported +++type MockData struct { +++ Data []byte +++ Index uint64 +++} +++ +++// MockStore exported +++type MockStore struct { +++ db map[string]*MockData +++} +++ +++// NewMockStore creates a Map backed Datastore that is useful for mocking +++func NewMockStore() *MockStore { +++ db := make(map[string]*MockData) +++ return &MockStore{db} +++} +++ +++// Get the value at "key", returns the last modified index +++// to use in conjunction to CAS calls +++func (s *MockStore) Get(key string) (*store.KVPair, error) { +++ mData := s.db[key] +++ if mData == nil { +++ return nil, nil +++ } +++ return &store.KVPair{Value: mData.Data, LastIndex: mData.Index}, nil +++ +++} +++ +++// Put a value at "key" +++func (s *MockStore) Put(key string, value []byte, options *store.WriteOptions) error { +++ mData := s.db[key] +++ if mData == nil { +++ mData = &MockData{value, 0} +++ } +++ mData.Index = mData.Index + 1 +++ s.db[key] = mData +++ return nil +++} +++ +++// Delete a value at "key" +++func (s *MockStore) Delete(key string) error { +++ delete(s.db, key) +++ return nil +++} +++ +++// Exists checks that the key exists inside the store +++func (s *MockStore) Exists(key string) (bool, error) { +++ _, ok := s.db[key] +++ return ok, nil +++} +++ +++// List gets a range of values at "directory" +++func (s *MockStore) List(prefix string) ([]*store.KVPair, error) { +++ return nil, ErrNotImplmented +++} +++ +++// DeleteTree deletes a range of values at "directory" +++func (s *MockStore) DeleteTree(prefix string) error { +++ delete(s.db, prefix) +++ return nil +++} +++ +++// Watch a single key for modifications +++func (s *MockStore) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) { +++ return nil, ErrNotImplmented +++} +++ +++// WatchTree triggers a watch on a range of values at "directory" +++func (s *MockStore) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) { +++ return nil, ErrNotImplmented +++} +++ +++// NewLock exposed +++func (s *MockStore) NewLock(key string, options *store.LockOptions) (store.Locker, error) { +++ return nil, ErrNotImplmented +++} +++ +++// AtomicPut put a value at "key" if the key has not been +++// modified in the meantime, throws an error if this is the case +++func (s *MockStore) AtomicPut(key string, newValue []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) { +++ mData := s.db[key] +++ +++ if previous == nil { +++ if mData != nil { +++ return false, nil, types.BadRequestErrorf("atomic put failed because key exists") +++ } // Else OK. +++ } else { +++ if mData == nil { +++ return false, nil, types.BadRequestErrorf("atomic put failed because key exists") +++ } +++ if mData != nil && mData.Index != previous.LastIndex { +++ return false, nil, types.BadRequestErrorf("atomic put failed due to mismatched Index") +++ } // Else OK. +++ } +++ err := s.Put(key, newValue, nil) +++ if err != nil { +++ return false, nil, err +++ } +++ return true, &store.KVPair{Key: key, Value: newValue, LastIndex: s.db[key].Index}, nil +++} +++ +++// AtomicDelete deletes a value at "key" if the key has not +++// been modified in the meantime, throws an error if this is the case +++func (s *MockStore) AtomicDelete(key string, previous *store.KVPair) (bool, error) { +++ mData := s.db[key] +++ if mData != nil && mData.Index != previous.LastIndex { +++ return false, types.BadRequestErrorf("atomic delete failed due to mismatched Index") +++ } +++ return true, s.Delete(key) +++} +++ +++// Close closes the client connection +++func (s *MockStore) Close() { +++ return +++} diff --cc libnetwork/docs/Vagrantfile index 00000000,00000000,00000000..8f1f8e1c new file mode 100644 --- /dev/null +++ b/libnetwork/docs/Vagrantfile @@@@ -1,0 -1,0 -1,0 +1,57 @@@@ +++# -*- mode: ruby -*- +++# vi: set ft=ruby : +++ +++# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! +++VAGRANTFILE_API_VERSION = "2" +++ +++$consul=<