Compare commits

..

176 Commits

Author SHA1 Message Date
Guillaume J. Charmes 4f202cd07f Bumped version to 0.3.0 2013-05-05 07:46:25 -07:00
Guillaume J. Charmes da01dd3d56 Merge remote-tracking branch 'origin/registry-update'
+ Registry: Add the new registry support
2013-05-05 07:13:25 -07:00
shin- 09f1cbabb9 Fixed imports 2013-05-06 11:06:44 -07:00
Sam Alba c9994ed0fb Moved the Debugf message in a registry to a more useful place 2013-05-06 10:57:48 -07:00
Guillaume J. Charmes b0e076f374 Add output to checksums, code cleaning 2013-05-06 10:57:46 -07:00
Sam Alba 00266df8ac Fixed public pull + Added some verbosity about what is happening 2013-05-06 10:57:45 -07:00
Sam Alba 3febeb93f5 Added help message to invite to login when getting a 401 2013-05-06 10:57:43 -07:00
Sam Alba bcdf03037b Fixed pulling repositories from library 2013-05-06 10:57:41 -07:00
Sam Alba a372f982c1 Switching to prod index server 2013-05-06 10:57:38 -07:00
shin- d985050aeb gofmt pass 2013-05-06 10:57:36 -07:00
shin- 0c5e76958b Use progress reader when uploading/downloading layers 2013-05-06 10:57:06 -07:00
Sam Alba 0f68042053 Handled wrong user credentials by re-init the auth file (it was impossible to login after having wrong crendentials) 2013-05-06 10:57:03 -07:00
shin- 18796d55a6 Fixed some login quirks 2013-05-06 10:57:00 -07:00
shin- 594827d416 Fixed typo in 'username or email already exists' 2013-05-06 10:56:59 -07:00
shin- 5690562fc8 Fix error in PushImage 2013-05-06 10:56:57 -07:00
shin- be791a223b simplify image.Checksum 2013-05-06 10:56:55 -07:00
shin- 19045b530e simplify graph.Checksums 2013-05-06 10:56:54 -07:00
shin- b5873806d0 Only send checksums for images not uploaded yet 2013-05-06 10:56:52 -07:00
shin- f10b0f75e0 Fix checksum computing 2013-05-06 10:56:50 -07:00
shin- 6e2ddf6f60 Checksum system overhaul 2013-05-06 10:56:49 -07:00
shin- e81a53eea9 Added support for REPO:TAG format in docker pull (overrides -t option) 2013-05-06 10:56:47 -07:00
shin- 1c76f91fc4 Fixed minor bugs in docker pull 2013-05-06 10:56:45 -07:00
shin- be75608906 Fixed checksum computing. Ensure checksum is computed when image metadata is loaded from disk. Fixed docker push workflow. Moved hash computing to utils 2013-05-06 10:56:10 -07:00
shin- 3c85e9390e Added X-Docker-Token header to initial index requests 2013-05-06 10:54:57 -07:00
shin- ea3374bcb0 Prepend hash method to the image checksum 2013-05-06 10:54:55 -07:00
shin- 6e936c8fd3 Follow redirections when sending PUT request in PushRepository 2013-05-06 10:54:53 -07:00
shin- 4cd9e4722c Fixed graph.Checksums() 2013-05-06 10:54:52 -07:00
shin- 630d358384 Fixed checksum representation 2013-05-06 10:54:50 -07:00
shin- 84be35dce1 Fixed docker login 2013-05-06 10:54:48 -07:00
shin- 23953e7d67 Style changes in auth.Login 2013-05-06 10:54:46 -07:00
shin- 6644a3c78a Reactivated CmdPush in commands.go 2013-05-06 10:54:45 -07:00
shin- e179c66400 Reimplemented docker pull for new registry API (command is still deactivated) 2013-05-06 10:54:41 -07:00
shin- 048fd671ef Implemented checksum computation on image creation (necessary for new push primitive) 2013-05-06 10:52:42 -07:00
shin- 7c1a27e2ad gofmt pass 2013-05-06 10:52:10 -07:00
shin- 1cf8a2c26c Changed some of the routes to reflect changes made to the API ; added HTTPClient singleton to the graph object 2013-05-06 10:52:08 -07:00
shin- e639309a7a Reimplemented feature: downloading all tags on a repository using docker pull. Temporarily commented out CmdPush 2013-05-06 10:51:10 -07:00
shin- 2f082510a7 Implemented new version of PullRepository. Missing support for whole repository pull (= no tag specified) 2013-05-06 10:51:07 -07:00
shin- 2421838b0a Support for the new registry/index API (wip) 2013-05-06 10:51:04 -07:00
Ken Cochrane 58ca46af39 Merge pull request #500 from kencochrane/registry-api-doc
added Docker Index search API doc
2013-05-06 10:44:50 -07:00
Ken Cochrane 09d4b9452d added new sphinx contrib extention for better REST API docs, and changed the index search API so that it uses the new docs, as a test to make sure it works correctly 2013-05-06 13:38:51 -04:00
Ken Cochrane d78b2d4ade Merge github.com:dotcloud/docker into registry-api-doc 2013-05-06 13:13:17 -04:00
Solomon Hykes e392b7ee9b Merge pull request #515 from drnic/vagrant_overrides
Allow reuse of existing vagrant boxes by env variables
2013-05-04 16:47:17 -07:00
Guillaume J. Charmes c3a5dd76cf Merge pull request #520 from dotcloud/519-no_command_specified-fix
- runtime: Fix the command existance check
2013-05-04 12:03:08 -07:00
Dr Nic Williams 04f41ebdbc Allow reuse of existing vagrant boxes by env variables
Usage:

    BOX_NAME=precise64 vagrant up
2013-05-03 15:35:51 -07:00
Guillaume J. Charmes c34989f1c4 Merge pull request #501 from losinggeneration/fix_cgroup_memory_check
- runtime: strings.Split may return an empty string on no match
2013-05-03 14:29:43 -07:00
Solomon Hykes a7c0e9a355 Fix a bug in the Makefile which caused dependency download to fail 2013-05-03 12:58:44 -07:00
Guillaume J. Charmes 19df5a7965 Merge pull request #487 from brunoqc/patch-2
* vagrant: Use only one deb line in /etc/apt
2013-05-03 11:55:16 -07:00
Harley Laue 589d7c68db Check that the line is valid with 6 parts after split 2013-05-03 13:49:10 -05:00
Guillaume J. Charmes 8ef72cbc94 Merge pull request #508 from bdon/master
* docs: doc fix
2013-05-03 11:41:14 -07:00
Guillaume J. Charmes b9ec03c21b Fix the command existance check 2013-05-02 20:50:28 -07:00
Brandon Liu 6cbe27b7a5 correct documentation for where images are stored on filesystem. 2013-05-02 20:37:08 -07:00
Solomon Hykes a82b60b30d dockerbuilder: change order of dependencies 2013-05-02 19:22:41 -07:00
Solomon Hykes c08d245539 dockerbuilder: let the Makefile upload to s3 with 'make release' 2013-05-02 18:11:54 -07:00
Guillaume J. Charmes 64d7bc442d Fix server crash when running an image without command without autorun 2013-05-02 13:56:45 -07:00
Harley Laue 4bc8ef42d4 strings.Split may return an empty string on no match
* This fixes an index out of range crash if cgroup memory is not
  enabled.
2013-05-02 14:44:41 -05:00
Guillaume J. Charmes 8378498951 Fix issue within mergeConfig preventing hostname and user to be set 2013-05-02 12:32:10 -07:00
Guillaume J. Charmes 1617a18258 Fix typo for command run docs 2013-05-02 12:11:57 -07:00
Solomon Hykes 6c1bb39c09 Fix date typos in changelog 2013-05-03 15:40:32 -07:00
Solomon Hykes f6b5cd77eb Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-05-03 15:37:37 -07:00
Solomon Hykes 5ffd63070f Bumped version to 0.2.2 2013-05-03 15:19:20 -07:00
Solomon Hykes 701132259d + Documentation: new example: sharing data between 2 couchdb databases 2013-05-03 15:13:12 -07:00
Solomon Hykes 18b8eeb484 + Support for data volumes 2013-05-03 13:03:47 -07:00
Solomon Hykes b6a5e604ab Add s3 upload to 'make release' 2013-05-02 11:32:55 -07:00
Ken Cochrane b682a8ea9e added Docker Index search API doc 2013-05-02 14:32:38 -04:00
Solomon Hykes 6e486b638b + Hack: 'make s3release' uploads a clean build to s3 2013-05-02 11:25:49 -07:00
Bruno Bigras 74cd7e822d Use only one deb line in /etc/apt
This prevents the script from filling up /etc/apt/sources.list with more than one deb line which cause a warning when updating.
2013-05-02 13:33:23 -04:00
Guillaume J. Charmes 21b9dcd518 Update docs for Command Run 2013-05-02 09:26:29 -07:00
Guillaume J. Charmes 897cc573f0 Fix the graph.Create prototype 2013-05-02 09:23:29 -07:00
Guillaume J. Charmes b0459adc27 Comply to the new graph.Create() prototype 2013-05-02 09:14:23 -07:00
Guillaume J. Charmes 3edd14b8c2 Implement the data volume removal 2013-05-02 09:14:23 -07:00
Guillaume J. Charmes 4099a31304 Implement the -volumes-from in order to mount volumes from an other container 2013-05-02 09:14:22 -07:00
Guillaume J. Charmes 6fb495bf6f Move the id of volumes to Container (instead of Container.Config) 2013-05-02 09:14:22 -07:00
Guillaume J. Charmes faf8daa7c6 Switch back config to map[string]struct{} 2013-05-02 09:14:22 -07:00
Guillaume J. Charmes 8d9aaee60b Handle data volumes mount points 2013-05-02 09:14:22 -07:00
Guillaume J. Charmes 35d704c8a0 Change the volumes type to map[string]string to store both source and destination 2013-05-02 09:14:22 -07:00
Solomon Hykes 1df5f4094b docker run -v PATH: bind a new data volume to a container 2013-05-02 09:14:22 -07:00
unclejack 528da23d6a use Go 1.0.3 to build docker 2013-05-02 15:27:37 +03:00
Guillaume J. Charmes ff5e238de9 Merge pull request #496 from dotcloud/480-vagrant-fix
- vagrant: Fix main Vagrantfile
2013-05-01 23:38:18 -07:00
Solomon Hykes c63dce393e Merge pull request #492 from kencochrane/registry-api-doc
+ Registry: added the registry API to the docker docs
2013-05-01 23:00:58 -07:00
Solomon Hykes d6a63132ef Merge branch 'origin/new-dockerbuilder' 2013-05-02 05:57:50 +00:00
Solomon Hykes e7271cdaae dockerbuilder: fix permissions 2013-05-02 05:56:51 +00:00
Solomon Hykes 6ca3b151b1 * Hack: improve the way dockerbuilder is built 2013-05-01 22:05:36 -07:00
Daniel Mizyrycki 0d9475346f Fix main Vagrantfile 2013-05-01 18:49:31 -07:00
Solomon Hykes 71199f595d New Dockerfile operation: 'add' 2013-05-01 18:32:38 -07:00
Solomon Hykes 58b95878f1 - Hack: fix dockerbuilder to build feature branches 2013-05-02 01:16:23 +00:00
Guillaume J. Charmes e431dc26f1 Merge pull request #482 from dotcloud/move_capabilitie_function
* runtime: Move the capabilities detection into a runtime method
2013-05-01 17:43:32 -07:00
Solomon Hykes 09b1cd58c0 Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-05-01 17:18:05 -07:00
Solomon Hykes d42639e5c5 Bumped version to 0.2.1 2013-05-01 17:17:13 -07:00
Guillaume J. Charmes d0c2e31fb9 Merge pull request #495 from dotcloud/autorun_docs
* docs: Update commandline Commit doc
2013-05-01 17:14:53 -07:00
Guillaume J. Charmes 509a01bbe4 Merge pull request #493 from dotcloud/374-developer-environment
* hack: development; issue #374: Refactor 'make hack' making Vagrantfile and VM more useful
2013-05-01 16:54:29 -07:00
Solomon Hykes e7fb7f13d5 new Dockerfile keyword: cmd to set a default runtime command 2013-05-01 16:43:37 -07:00
Daniel Mizyrycki d172da58ce development; issue #374: Update VM documentation 2013-05-01 15:59:54 -07:00
Solomon Hykes ad86dde10c * 'docker commit' inherits parent layer's run configuration by default 2013-05-01 15:45:39 -07:00
Guillaume J. Charmes c20e46587d Update commandline Commit doc 2013-05-01 15:43:02 -07:00
Daniel Mizyrycki eeb03164cf development; issue #374: Upgrade development VM box to Ubuntu-13.04 with kernel 3.8 2013-05-01 15:26:27 -07:00
Daniel Mizyrycki bb61678b57 development; issue #374: Refactor 'make hack' making Vagrantfile and VM more useful 2013-05-01 15:26:27 -07:00
Guillaume J. Charmes a75a1b3859 When no -config is set while committing, use the config of the base image 2013-05-01 15:24:28 -07:00
Solomon Hykes 08812096f5 New Dockerfile operation 'expose' exposes default tcp ports 2013-05-01 14:16:56 -07:00
Solomon Hykes 5c30faf6f7 Set a layer's default runtime options with 'docker commit -run' instead of 'docker commit -config' 2013-05-01 12:45:45 -07:00
Solomon Hykes f7aaa06606 + Commit default runtime options with a layer 2013-05-01 11:33:21 -07:00
Guillaume J. Charmes 7ff65d40d5 Actually use the mergeConfig function 2013-05-01 11:22:06 -07:00
Ken Cochrane 904c2a0fc3 added the registry API to the docker docs 2013-05-01 12:31:46 -04:00
Solomon Hykes a3ce90b78b Added dummy script for docker-build example 2013-05-01 00:49:28 -07:00
Solomon Hykes 03b83b3210 Fix example dockerfile 2013-05-01 00:44:36 -07:00
Solomon Hykes 40ccf1d300 new Dockerfile keyword: 'push' 2013-05-01 00:42:11 -07:00
Solomon Hykes 038ca5ee39 docker-build: added support for 'maintainer' keyword 2013-05-01 00:14:52 -07:00
Solomon Hykes 957c500ac9 Merge pull request #485 from brunoqc/patch-1
* Packaging: connect to Ubuntu key server on port 80
2013-04-30 15:46:57 -07:00
Guillaume J. Charmes 62a595da5c Merge pull request #488 from tobert/cgroups-via-proc-mounts
* runtime: Use /proc/mounts instead of mount(8)
2013-04-30 14:39:14 -07:00
Guillaume J. Charmes d97661aa71 Improve crashTest 2013-04-30 11:16:26 -07:00
Al Tobey c6119da339 Use /proc/mounts instead of mount(8)
Specifically, Ubuntu Precise's cgroup-lite script uses mount -n
to mount the cgroup filesystems so they don't appear in mtab, so
detection always fails unless the admin updates mtab with /proc/mounts.

/proc/mounts is valid on just about every Linux machine in existence and
as a bonus is much easier to parse.

I also removed the regex in favor of a more accurate parser that should
also support monolitic cgroup mounts (e.g. mount -t cgroup none /cgroup).
2013-04-30 17:37:43 +00:00
Bruno Bigras 5051c20833 Use the 80 port with keyserver.ubuntu.com
Use the 80 port with keyserver.ubuntu.com so it works with corporate firewalls
2013-04-29 15:53:50 -03:00
Guillaume J. Charmes cdc2657ee9 Improve crashTest 2013-04-28 07:10:58 -07:00
Guillaume J. Charmes 76a1a7cf5b Simplify the crashTest 2013-04-28 06:23:02 -07:00
Guillaume J. Charmes 20c2a4f80f add network endpoint for crashTest 2013-04-28 03:54:22 -07:00
Guillaume J. Charmes ebe157ebb5 Update the crashTest to have the dockerpath in env 2013-04-28 01:27:56 -07:00
Solomon Hykes cb431f223f Merge pull request #484 from tianon/mkimage-debian
* Contrib: updated mkimage-debian
2013-04-29 12:12:02 -07:00
Tianon Gravi ab34115b42 Use default mirror from debootstrap when not explicitly provided, and add better target directory naming 2013-04-28 13:38:26 -06:00
Tianon Gravi 4b3354af3f Improve mkimage-debian script to also tag using the release version number of the final image (6.0.7, 7.0, etc.)
This is as discussed on #447.
2013-04-28 12:31:28 -06:00
Guillaume J. Charmes 9042535f5a Move the capabilities detection into a runtime method 2013-04-26 14:32:55 -07:00
Guillaume J. Charmes 8f81e175af Merge pull request #473 from dotcloud/26-auto_restart_containers-feature
+ runtime: Add -r flag to dockerd in order to restart previously running container....
2013-04-26 14:02:01 -07:00
Guillaume J. Charmes 636c7835d3 Merge pull request #467 from dotcloud/improve_localhost_port_test
* tests: Improve unit test to avoid unnecessary warnigns
2013-04-26 14:01:13 -07:00
Guillaume J. Charmes 6d1dd8b41a Merge pull request #478 from tianon/mkimage-debian
+ contrib: Add contrib/mkimage-debian.sh used to create the tianon/debian images
2013-04-26 13:51:47 -07:00
Guillaume J. Charmes ae97477284 Remove -command in CmdCommit and make -config use Json 2013-04-26 10:48:33 -07:00
Tianon Gravi 86ad98e72a Add contrib/mkimage-debian.sh used to create the tianon/debian images 2013-04-26 08:54:29 -06:00
Solomon Hykes 03d82922aa Merge pull request #474 from brianm/vmware_fusion_provider
Support for VMWare Fusion Provider in Vagrantfile
2013-04-26 01:31:11 -07:00
Guillaume J. Charmes 30d327d37e Add TestCommitAutoRun 2013-04-25 17:03:13 -07:00
Guillaume J. Charmes 724e2d6b0a Update unit test in order to comply with new api 2013-04-25 17:02:38 -07:00
Guillaume J. Charmes 51d6228261 Implement -config and -command in CmdCommit in order to allow autorun 2013-04-25 16:48:31 -07:00
Brian McCallister 4db680fda4 don't fight the box kernel version, not worth it 2013-04-25 06:29:13 -06:00
Brian McCallister 9c7293508d get aufs dependencies into vmware image 2013-04-25 06:09:04 -06:00
Brian McCallister 9d8743a7ae vmware fusion provider config 2013-04-25 05:59:31 -06:00
Guillaume J. Charmes 50144aeb42 Add -r flag to dockerd in order to restart previously running container. Fixes #26 2013-04-24 19:01:23 -07:00
Guillaume J. Charmes ee298d1420 Specify a different bridge for tests than for regular runtime 2013-04-24 17:43:41 -07:00
Solomon Hykes 03855b0027 Merge pull request #466 from dotcloud/441-vagrant-improve
* Packaging: simplify Vagrantfile
2013-04-24 17:25:20 -07:00
Daniel Mizyrycki 2726e3649a vagrant; issue #441: Improve main config including aws ubuntu lts dependency 2013-04-24 11:30:15 -07:00
Solomon Hykes 90668a8a99 Bumped version to 0.2.0 2013-04-23 23:15:09 -07:00
Solomon Hykes c7fd84b8a0 Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-04-23 23:05:38 -07:00
Solomon Hykes 874a40ed3a - Dev: dockerbuilder requires a fake initctl because 'apt-get install devscripts' insists on installing a stupid daemon I never asked for in the first place. 2013-04-23 23:04:54 -07:00
Solomon Hykes 370fafacbf Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-04-23 22:57:50 -07:00
Solomon Hykes a0478f726d dockerbuilder: upload most recent Ubuntu package (note version FOO might not yet be packaged at tag vFOO) 2013-04-23 22:57:34 -07:00
Solomon Hykes e5bc5a2e31 Merge pull request #427 from dhrp/docs
- Packaging: Fixed Vagrantfile
* Documentation: Updated install instructions
2013-04-23 19:49:28 -07:00
Solomon Hykes 25fc3a7e76 Merge pull request #470 from dotcloud/packaging-ubuntu
* Packaging: Add 0.1.8 to Ubuntu packaging changelog
* Packaging: Update the Ubuntu maintainer manual
2013-04-23 19:46:56 -07:00
Solomon Hykes b3ab0b561e Makefile improvements
+ Convenience rules: srcrelease, deps
	- Separate dependency vendoring from building the binary
	  (re-download dependencies with 'make deps')
2013-04-23 19:41:38 -07:00
Solomon Hykes 8b8c8bf7cb Fix 'make release RELEASE_VERSION=master' 2013-04-23 18:50:53 -07:00
Solomon Hykes a8651a23b2 make release: build a binary release of the most recent version tag 2013-04-23 18:32:59 -07:00
Daniel Mizyrycki f744cfd5a7 packaging-ubuntu: update maintainer documentation for changelog file 2013-04-23 13:51:03 -07:00
Solomon Hykes e03b241fb1 dockerbuilder: build with 'make; cp -R ./bin' 2013-04-23 12:07:54 -07:00
Thatcher Peskens 1ddca1948b Fixed remaining issues and conflicts created by last merge. 2013-04-23 12:04:53 -07:00
Solomon Hykes 2485bb2cd2 dockerbuilder: use a pristine GOPATH, with the fresh checkout registered at the right path (for internal submodules) 2013-04-23 11:45:47 -07:00
Guillaume J. Charmes 6ebb249131 Remove unecessary memeory limit within tests 2013-04-23 11:25:16 -07:00
Guillaume J. Charmes c45beabcd5 Improve TestMultipleAttachRestart to avoid unnecessary warning 2013-04-23 11:22:30 -07:00
Guillaume J. Charmes a22c78523f Wait for the container to finish in TestAttachDisconnect before destroying it 2013-04-23 11:09:48 -07:00
Guillaume J. Charmes 5a02c9ba0a Make sure the container is well started prior to perform the test 2013-04-23 11:08:31 -07:00
Solomon Hykes 7577f48dc4 dockerbuilder: build in current directory instead /go and /tmp 2013-04-23 10:53:02 -07:00
Solomon Hykes 0512cf9c83 dockerbuilder: /usr/local/bin is already set by docker 2013-04-23 10:49:58 -07:00
Solomon Hykes 73da7a12e7 Increased timeout in TCP port allocation test to pass on slower machines 2013-04-23 10:12:46 -07:00
Solomon Hykes 50f5723f1d Merge pull request #465 from shamrin/patch-2
- Documentation: fixed typo in "Building blocks"
2013-04-23 09:02:19 -07:00
Alexey Shamrin cbc4eccd50 fixed typo in buildingblocks.rst 2013-04-23 12:52:55 +04:00
Solomon Hykes cff26b3a6c Merge pull request #464 from tianon/patch-1
- Runtime: adapt cgroup capability detection to work on Gentoo
2013-04-23 00:36:10 -07:00
Solomon Hykes 329c3e0ffd Merge pull request #462 from dotcloud/initial_changelog
+ Add initial Changelog
2013-04-23 00:32:25 -07:00
Solomon Hykes 4f6cc5c733 Completed Changelog for all past versions 2013-04-23 00:30:18 -07:00
Tianon Gravi e413340723 Update FindCgroupMountpoint to be more forgiving
On Gentoo, the memory cgroup is mounted at /sys/fs/cgroup/memory, but the mount line looks like the following:
memory on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)

(note that the first word on the line is "memory", not "cgroup", but the other essentials are there, namely the type of cgroup and the memory mount option)
2013-04-23 01:09:29 -06:00
Solomon Hykes 95e066d24f - Runtime: ghost containers can be killed. 2013-04-22 22:30:33 -07:00
Solomon Hykes 82b8f7a565 hack/dockerbuilder: a standard build environment for building and uploading official binary builds of docker... inside docker 2013-04-22 22:29:12 -07:00
Thatcher Peskens 5a5e417d46 Merge remote-tracking branch 'dotcloud/master' into dhrp/docs
Conflicts:
	docker/docker.go
	docs/sources/installation/archlinux.rst
	docs/sources/installation/index.rst
	docs/sources/installation/ubuntulinux.rst
	runtime.go
	utils.go
2013-04-22 18:44:50 -07:00
Thatcher Peskens 4031a01af1 Merged changes 2013-04-22 18:38:42 -07:00
Guillaume J. Charmes b76d63cb0c Forbid attach to ghost 2013-04-22 17:53:32 -07:00
Guillaume J. Charmes f926ed182f Allow to kill/stop ghosts 2013-04-22 17:53:32 -07:00
Guillaume J. Charmes d440782e17 Allow to kill container after docker server restarts 2013-04-22 17:52:38 -07:00
Guillaume J. Charmes 82848d4158 Allow to wait on container even after docker server restarts using lxc-info 2013-04-22 17:52:38 -07:00
Guillaume J. Charmes 0b60829df7 Add initial changelog 2013-04-22 15:26:06 -07:00
Thatcher Peskens 690e118670 Updated gettingstarted with quicker install. 2013-04-22 13:36:00 -07:00
Thatcher Peskens 6c8dcd5cbb Updated Vagrantfile and documentation to reflect new installation path using Ubuntu's PPA, also switched everything to use Ubuntu 12.04 by default. 2013-04-22 13:10:32 -07:00
Thatcher Peskens 0731d1a582 Updated ubuntu install 2013-04-19 20:59:43 -07:00
Thatcher Peskens 8ecde8f9a5 Updated documentation and fixed Vagrantfile 2013-04-19 20:57:50 -07:00
Thatcher Peskens 6de5ca1e64 Added redirect from old location of documentation (/documentation), these was the location when we were on github. 2013-04-18 16:00:18 -07:00
70 changed files with 2504 additions and 735 deletions
+111
View File
@@ -0,0 +1,111 @@
# Changelog
## 0.3.0 (2013-05-06)
+ Registry: Implement the new registry
+ Documentation: new example: sharing data between 2 couchdb databases
- Runtime: Fix the command existance check
- Runtime: strings.Split may return an empty string on no match
- Runtime: Fix an index out of range crash if cgroup memory is not
* Documentation: Various improvments
* Vagrant: Use only one deb line in /etc/apt
## 0.2.2 (2013-05-03)
+ Support for data volumes ('docker run -v=PATH')
+ Share data volumes between containers ('docker run -volumes-from')
+ Improved documentation
* Upgrade to Go 1.0.3
* Various upgrades to the dev environment for contributors
## 0.2.1 (2013-05-01)
+ 'docker commit -run' bundles a layer with default runtime options: command, ports etc.
* Improve install process on Vagrant
+ New Dockerfile operation: "maintainer"
+ New Dockerfile operation: "expose"
+ New Dockerfile operation: "cmd"
+ Contrib script to build a Debian base layer
+ 'docker -d -r': restart crashed containers at daemon startup
* Runtime: improve test coverage
## 0.2.0 (2013-04-23)
- Runtime: ghost containers can be killed and waited for
* Documentation: update install intructions
- Packaging: fix Vagrantfile
- Development: automate releasing binaries and ubuntu packages
+ Add a changelog
- Various bugfixes
## 0.1.8 (2013-04-22)
- Dynamically detect cgroup capabilities
- Issue stability warning on kernels <3.8
- 'docker push' buffers on disk instead of memory
- Fix 'docker diff' for removed files
- Fix 'docker stop' for ghost containers
- Fix handling of pidfile
- Various bugfixes and stability improvements
## 0.1.7 (2013-04-18)
- Container ports are available on localhost
- 'docker ps' shows allocated TCP ports
- Contributors can run 'make hack' to start a continuous integration VM
- Streamline ubuntu packaging & uploading
- Various bugfixes and stability improvements
## 0.1.6 (2013-04-17)
- Record the author an image with 'docker commit -author'
## 0.1.5 (2013-04-17)
- Disable standalone mode
- Use a custom DNS resolver with 'docker -d -dns'
- Detect ghost containers
- Improve diagnosis of missing system capabilities
- Allow disabling memory limits at compile time
- Add debian packaging
- Documentation: installing on Arch Linux
- Documentation: running Redis on docker
- Fixed lxc 0.9 compatibility
- Automatically load aufs module
- Various bugfixes and stability improvements
## 0.1.4 (2013-04-09)
- Full support for TTY emulation
- Detach from a TTY session with the escape sequence `C-p C-q`
- Various bugfixes and stability improvements
- Minor UI improvements
- Automatically create our own bridge interface 'docker0'
## 0.1.3 (2013-04-04)
- Choose TCP frontend port with '-p :PORT'
- Layer format is versioned
- Major reliability improvements to the process manager
- Various bugfixes and stability improvements
## 0.1.2 (2013-04-03)
- Set container hostname with 'docker run -h'
- Selective attach at run with 'docker run -a [stdin[,stdout[,stderr]]]'
- Various bugfixes and stability improvements
- UI polish
- Progress bar on push/pull
- Use XZ compression by default
- Make IP allocator lazy
## 0.1.1 (2013-03-31)
- Display shorthand IDs for convenience
- Stabilize process management
- Layers can include a commit message
- Simplified 'docker attach'
- Fixed support for re-attaching
- Various bugfixes and stability improvements
- Auto-download at run
- Auto-login on push
- Beefed up documentation
## 0.1.0 (2013-03-23)
- First release
- Implement registry in order to push/pull images
- TCP port allocation
- Fix termcaps on Linux
- Add documentation
- Add Vagrant support with Vagrantfile
- Add unit tests
- Add repository/tags to ease image management
- Improve the layer implementation
+32 -4
View File
@@ -1,5 +1,9 @@
DOCKER_PACKAGE := github.com/dotcloud/docker
RELEASE_VERSION := $(shell git tag | grep -E "v[0-9\.]+$$" | sort -nr | head -n 1)
SRCRELEASE := docker-$(RELEASE_VERSION)
BINRELEASE := docker-$(RELEASE_VERSION).tgz
GIT_ROOT := $(shell git rev-parse --show-toplevel)
BUILD_DIR := $(CURDIR)/.gopath
GOPATH ?= $(BUILD_DIR)
@@ -23,18 +27,39 @@ DOCKER_MAIN := $(DOCKER_DIR)/docker
DOCKER_BIN_RELATIVE := bin/docker
DOCKER_BIN := $(CURDIR)/$(DOCKER_BIN_RELATIVE)
.PHONY: all clean test hack
.PHONY: all clean test hack release srcrelease $(BINRELEASE) $(SRCRELEASE) $(DOCKER_BIN) $(DOCKER_DIR)
all: $(DOCKER_BIN)
$(DOCKER_BIN): $(DOCKER_DIR)
@mkdir -p $(dir $@)
@(cd $(DOCKER_MAIN); go get $(GO_OPTIONS); go build $(GO_OPTIONS) $(BUILD_OPTIONS) -o $@)
@(cd $(DOCKER_MAIN); go build $(GO_OPTIONS) $(BUILD_OPTIONS) -o $@)
@echo $(DOCKER_BIN_RELATIVE) is created.
$(DOCKER_DIR):
@mkdir -p $(dir $@)
@ln -sf $(CURDIR)/ $@
@if [ -h $@ ]; then rm -f $@; fi; ln -sf $(CURDIR)/ $@
@(cd $(DOCKER_MAIN); go get $(GO_OPTIONS))
whichrelease:
echo $(RELEASE_VERSION)
release: $(BINRELEASE)
s3cmd -P put $(BINRELEASE) s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-$(RELEASE_VERSION).tgz
srcrelease: $(SRCRELEASE)
deps: $(DOCKER_DIR)
# A clean checkout of $RELEASE_VERSION, with vendored dependencies
$(SRCRELEASE):
rm -fr $(SRCRELEASE)
git clone $(GIT_ROOT) $(SRCRELEASE)
cd $(SRCRELEASE); git checkout -q $(RELEASE_VERSION)
# A binary release ready to be uploaded to a mirror
$(BINRELEASE): $(SRCRELEASE)
rm -f $(BINRELEASE)
cd $(SRCRELEASE); make; cp -R bin docker-$(RELEASE_VERSION); tar -f ../$(BINRELEASE) -zv -c docker-$(RELEASE_VERSION)
clean:
@rm -rf $(dir $(DOCKER_BIN))
@@ -51,4 +76,7 @@ fmt:
@gofmt -s -l -w .
hack:
cd $(CURDIR)/buildbot && vagrant up
cd $(CURDIR)/hack && vagrant up
ssh-dev:
cd $(CURDIR)/hack && vagrant ssh
Vendored
+22 -26
View File
@@ -1,41 +1,37 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
def v10(config)
config.vm.box = "quantal64_3.5.0-25"
config.vm.box_url = "http://get.docker.io/vbox/ubuntu/12.10/quantal64_3.5.0-25.box"
BOX_NAME = ENV['BOX_NAME'] || "ubuntu"
BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box"
PPA_KEY = "E61D797F63561DC6"
config.vm.share_folder "v-data", "/opt/go/src/github.com/dotcloud/docker", File.dirname(__FILE__)
# Ensure puppet is installed on the instance
config.vm.provision :shell, :inline => "apt-get -qq update; apt-get install -y puppet"
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "puppet/manifests"
puppet.manifest_file = "quantal64.pp"
puppet.module_path = "puppet/modules"
Vagrant::Config.run do |config|
# Setup virtual machine box. This VM configuration code is always executed.
config.vm.box = BOX_NAME
config.vm.box_url = BOX_URI
# Add docker PPA key to the local repository and install docker
pkg_cmd = "apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys #{PPA_KEY}; "
pkg_cmd << "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >/etc/apt/sources.list.d/lxc-docker.list; "
pkg_cmd << "apt-get update -qq; apt-get install -q -y lxc-docker"
if ARGV.include?("--provider=aws".downcase)
# Add AUFS dependency to amazon's VM
pkg_cmd << "; apt-get install linux-image-extra-3.2.0-40-virtual"
end
config.vm.provision :shell, :inline => pkg_cmd
end
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
v10(config)
end
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("1") do |config|
v10(config)
end
# Providers were added on Vagrant >= 1.1.0
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
config.vm.provider :aws do |aws|
config.vm.provider :aws do |aws, override|
config.vm.box = "dummy"
config.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
aws.access_key_id = ENV["AWS_ACCESS_KEY_ID"]
aws.secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"]
aws.keypair_name = ENV["AWS_KEYPAIR_NAME"]
aws.ssh_private_key_path = ENV["AWS_SSH_PRIVKEY"]
override.ssh.private_key_path = ENV["AWS_SSH_PRIVKEY"]
override.ssh.username = "ubuntu"
aws.region = "us-east-1"
aws.ami = "ami-ae9806c7"
aws.ssh_username = "ubuntu"
aws.ami = "ami-d0f89fb9"
aws.instance_type = "t1.micro"
end
@@ -51,7 +47,7 @@ Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
end
config.vm.provider :virtualbox do |vb|
config.vm.box = "quantal64_3.5.0-25"
config.vm.box_url = "http://get.docker.io/vbox/ubuntu/12.10/quantal64_3.5.0-25.box"
config.vm.box = BOX_NAME
config.vm.box_url = BOX_URI
end
end
+29 -23
View File
@@ -3,7 +3,6 @@ package auth
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
@@ -16,7 +15,7 @@ import (
const CONFIGFILE = ".dockercfg"
// the registry server we want to login against
const REGISTRY_SERVER = "https://registry.docker.io"
const INDEX_SERVER = "https://index.docker.io"
type AuthConfig struct {
Username string `json:"username"`
@@ -76,6 +75,9 @@ func LoadConfig(rootPath string) (*AuthConfig, error) {
return nil, err
}
arr := strings.Split(string(b), "\n")
if len(arr) < 2 {
return nil, fmt.Errorf("The Auth config file is empty")
}
origAuth := strings.Split(arr[0], " = ")
origEmail := strings.Split(arr[1], " = ")
authConfig, err := DecodeAuth(origAuth[1])
@@ -89,9 +91,14 @@ func LoadConfig(rootPath string) (*AuthConfig, error) {
// save the auth config
func saveConfig(rootPath, authStr string, email string) error {
confFile := path.Join(rootPath, CONFIGFILE)
if len(email) == 0 {
os.Remove(confFile)
return nil
}
lines := "auth = " + authStr + "\n" + "email = " + email + "\n"
b := []byte(lines)
err := ioutil.WriteFile(path.Join(rootPath, CONFIGFILE), b, 0600)
err := ioutil.WriteFile(confFile, b, 0600)
if err != nil {
return err
}
@@ -101,40 +108,38 @@ func saveConfig(rootPath, authStr string, email string) error {
// try to register/login to the registry server
func Login(authConfig *AuthConfig) (string, error) {
storeConfig := false
client := &http.Client{}
reqStatusCode := 0
var status string
var errMsg string
var reqBody []byte
jsonBody, err := json.Marshal(authConfig)
if err != nil {
errMsg = fmt.Sprintf("Config Error: %s", err)
return "", errors.New(errMsg)
return "", fmt.Errorf("Config Error: %s", err)
}
// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
b := strings.NewReader(string(jsonBody))
req1, err := http.Post(REGISTRY_SERVER+"/v1/users", "application/json; charset=utf-8", b)
req1, err := http.Post(INDEX_SERVER+"/v1/users/", "application/json; charset=utf-8", b)
if err != nil {
errMsg = fmt.Sprintf("Server Error: %s", err)
return "", errors.New(errMsg)
return "", fmt.Errorf("Server Error: %s", err)
}
reqStatusCode = req1.StatusCode
defer req1.Body.Close()
reqBody, err = ioutil.ReadAll(req1.Body)
if err != nil {
errMsg = fmt.Sprintf("Server Error: [%#v] %s", reqStatusCode, err)
return "", errors.New(errMsg)
return "", fmt.Errorf("Server Error: [%#v] %s", reqStatusCode, err)
}
if reqStatusCode == 201 {
status = "Account Created\n"
status = "Account created. Please use the confirmation link we sent" +
" to your e-mail to activate it.\n"
storeConfig = true
} else if reqStatusCode == 403 {
return "", fmt.Errorf("Login: Your account hasn't been activated. " +
"Please check your e-mail for a confirmation link.")
} else if reqStatusCode == 400 {
// FIXME: This should be 'exists', not 'exist'. Need to change on the server first.
if string(reqBody) == "Username or email already exist" {
client := &http.Client{}
req, err := http.NewRequest("GET", REGISTRY_SERVER+"/v1/users", nil)
if string(reqBody) == "\"Username or email already exists\"" {
req, err := http.NewRequest("GET", INDEX_SERVER+"/v1/users/", nil)
req.SetBasicAuth(authConfig.Username, authConfig.Password)
resp, err := client.Do(req)
if err != nil {
@@ -148,17 +153,18 @@ func Login(authConfig *AuthConfig) (string, error) {
if resp.StatusCode == 200 {
status = "Login Succeeded\n"
storeConfig = true
} else if resp.StatusCode == 401 {
saveConfig(authConfig.rootPath, "", "")
return "", fmt.Errorf("Wrong login/password, please try again")
} else {
status = fmt.Sprintf("Login: %s", body)
return "", errors.New(status)
return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
resp.StatusCode, resp.Header)
}
} else {
status = fmt.Sprintf("Registration: %s", reqBody)
return "", errors.New(status)
return "", fmt.Errorf("Registration: %s", reqBody)
}
} else {
status = fmt.Sprintf("[%s] : %s", reqStatusCode, reqBody)
return "", errors.New(status)
return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
}
if storeConfig {
authStr := EncodeAuth(authConfig)
+81 -20
View File
@@ -10,6 +10,7 @@ import (
"log"
"net/http"
"net/url"
"path/filepath"
"runtime"
"strconv"
"strings"
@@ -18,7 +19,7 @@ import (
"unicode"
)
const VERSION = "0.1.8"
const VERSION = "0.3.0"
var (
GIT_COMMIT string
@@ -400,7 +401,8 @@ func (srv *Server) CmdHistory(stdin io.ReadCloser, stdout io.Writer, args ...str
}
func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "rm", "CONTAINER [CONTAINER...]", "Remove a container")
cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove a container")
v := cmd.Bool("v", false, "Remove the volumes associated to the container")
if err := cmd.Parse(args); err != nil {
return nil
}
@@ -408,15 +410,40 @@ func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string)
cmd.Usage()
return nil
}
volumes := make(map[string]struct{})
for _, name := range cmd.Args() {
container := srv.runtime.Get(name)
if container == nil {
return fmt.Errorf("No such container: %s", name)
}
// Store all the deleted containers volumes
for _, volumeId := range container.Volumes {
volumes[volumeId] = struct{}{}
}
if err := srv.runtime.Destroy(container); err != nil {
fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error())
}
}
if *v {
// Retrieve all volumes from all remaining containers
usedVolumes := make(map[string]*Container)
for _, container := range srv.runtime.List() {
for _, containerVolumeId := range container.Volumes {
usedVolumes[containerVolumeId] = container
}
}
for volumeId := range volumes {
// If the requested volu
if c, exists := usedVolumes[volumeId]; exists {
fmt.Fprintf(stdout, "The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.Id)
continue
}
if err := srv.runtime.volumes.Delete(volumeId); err != nil {
return err
}
}
}
return nil
}
@@ -477,7 +504,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args .
}
archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout, "Importing %v/%v (%v)")
}
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "")
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
if err != nil {
return err
}
@@ -494,6 +521,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args .
func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
cmd := rcli.Subcmd(stdout, "push", "NAME", "Push an image or a repository to the registry")
registry := cmd.String("registry", "", "Registry host to push the image to")
if err := cmd.Parse(args); err != nil {
return nil
}
@@ -504,8 +532,8 @@ func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...
return nil
}
// If the login failed, abort
if srv.runtime.authConfig == nil || srv.runtime.authConfig.Username == "" {
// If the login failed AND we're using the index, abort
if *registry == "" && (srv.runtime.authConfig == nil || srv.runtime.authConfig.Username == "") {
if err := srv.CmdLogin(stdin, stdout, args...); err != nil {
return err
}
@@ -528,9 +556,6 @@ func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...
Debugf("Pushing [%s] to [%s]\n", local, remote)
// Try to get the image
// FIXME: Handle lookup
// FIXME: Also push the tags in case of ./docker push myrepo:mytag
// img, err := srv.runtime.LookupImage(cmd.Arg(0))
img, err := srv.runtime.graph.Get(local)
if err != nil {
Debugf("The push refers to a repository [%s] (len: %d)\n", local, len(srv.runtime.repositories.Repositories[local]))
@@ -544,7 +569,7 @@ func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...
return err
}
err = srv.runtime.graph.PushImage(stdout, img, srv.runtime.authConfig)
err = srv.runtime.graph.PushImage(stdout, img, *registry, nil)
if err != nil {
return err
}
@@ -553,6 +578,8 @@ func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...
func (srv *Server) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "pull", "NAME", "Pull an image or a repository from the registry")
tag := cmd.String("t", "", "Download tagged image in repository")
registry := cmd.String("registry", "", "Registry to download from. Necessary if image is pulled by ID")
if err := cmd.Parse(args); err != nil {
return nil
}
@@ -562,15 +589,20 @@ func (srv *Server) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string
return nil
}
if strings.Contains(remote, ":") {
remoteParts := strings.Split(remote, ":")
tag = &remoteParts[1]
remote = remoteParts[0]
}
// FIXME: CmdPull should be a wrapper around Runtime.Pull()
if srv.runtime.graph.LookupRemoteImage(remote, srv.runtime.authConfig) {
if err := srv.runtime.graph.PullImage(stdout, remote, srv.runtime.authConfig); err != nil {
if *registry != "" {
if err := srv.runtime.graph.PullImage(stdout, remote, *registry, nil); err != nil {
return err
}
return nil
}
// FIXME: Allow pull repo:tag
if err := srv.runtime.graph.PullRepository(stdout, remote, "", srv.runtime.repositories, srv.runtime.authConfig); err != nil {
if err := srv.runtime.graph.PullRepository(stdout, remote, *tag, srv.runtime.repositories, srv.runtime.authConfig); err != nil {
return err
}
return nil
@@ -726,6 +758,7 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
"Create a new image from a container's changes")
flComment := cmd.String("m", "", "Commit message")
flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
flConfig := cmd.String("run", "", "Config automatically applied when the image is run. "+`(ex: {"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`)
if err := cmd.Parse(args); err != nil {
return nil
}
@@ -734,7 +767,16 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
cmd.Usage()
return nil
}
img, err := srv.runtime.Commit(containerName, repository, tag, *flComment, *flAuthor)
var config *Config
if *flConfig != "" {
config = &Config{}
if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
return err
}
}
img, err := srv.runtime.Commit(containerName, repository, tag, *flComment, *flAuthor, config)
if err != nil {
return err
}
@@ -836,6 +878,10 @@ func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout rcli.DockerConn, args .
return fmt.Errorf("No such container: %s", name)
}
if container.State.Ghost {
return fmt.Errorf("Impossible to attach to a ghost container")
}
if container.Config.Tty {
stdout.SetOptionRawTerminal()
}
@@ -899,6 +945,25 @@ func (opts AttachOpts) Get(val string) bool {
return false
}
// PathOpts stores a unique set of absolute paths
type PathOpts map[string]struct{}
func NewPathOpts() PathOpts {
return make(PathOpts)
}
func (opts PathOpts) String() string {
return fmt.Sprintf("%v", map[string]struct{}(opts))
}
func (opts PathOpts) Set(val string) error {
if !filepath.IsAbs(val) {
return fmt.Errorf("%s is not an absolute path", val)
}
opts[filepath.Clean(val)] = struct{}{}
return nil
}
func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
force := cmd.Bool("f", false, "Force")
@@ -921,10 +986,6 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
fmt.Fprintln(stdout, "Error: Image not specified")
return fmt.Errorf("Image not specified")
}
if len(config.Cmd) == 0 {
fmt.Fprintln(stdout, "Error: Command not specified")
return fmt.Errorf("Command not specified")
}
if config.Tty {
stdout.SetOptionRawTerminal()
@@ -989,11 +1050,11 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
return nil
}
func NewServer() (*Server, error) {
func NewServer(autoRestart bool) (*Server, error) {
if runtime.GOARCH != "amd64" {
log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
}
runtime, err := NewRuntime()
runtime, err := NewRuntime(autoRestart)
if err != nil {
return nil, err
}
+1
View File
@@ -394,4 +394,5 @@ func TestAttachDisconnect(t *testing.T) {
// Try to avoid the timeoout in destroy. Best effort, don't check error
cStdin, _ := container.StdinPipe()
cStdin.Close()
container.Wait()
}
+99 -11
View File
@@ -48,6 +48,7 @@ type Container struct {
runtime *Runtime
waitLock chan struct{}
Volumes map[string]string
}
type Config struct {
@@ -66,6 +67,8 @@ type Config struct {
Cmd []string
Dns []string
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
Volumes map[string]struct{}
VolumesFrom string
}
func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Config, error) {
@@ -97,6 +100,11 @@ func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Con
var flDns ListOpts
cmd.Var(&flDns, "dns", "Set custom dns servers")
flVolumes := NewPathOpts()
cmd.Var(flVolumes, "v", "Attach a data volume")
flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
if err := cmd.Parse(args); err != nil {
return nil, err
}
@@ -136,6 +144,8 @@ func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Con
Cmd: runCmd,
Dns: flDns,
Image: image,
Volumes: flVolumes,
VolumesFrom: *flVolumesFrom,
}
if *flMemory > 0 && !capabilities.SwapLimit {
@@ -394,10 +404,40 @@ func (container *Container) Start() error {
log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
container.Config.MemorySwap = -1
}
container.Volumes = make(map[string]string)
// Create the requested volumes volumes
for volPath := range container.Config.Volumes {
if c, err := container.runtime.volumes.Create(nil, container, "", "", nil); err != nil {
return err
} else {
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
return nil
}
container.Volumes[volPath] = c.Id
}
}
if container.Config.VolumesFrom != "" {
c := container.runtime.Get(container.Config.VolumesFrom)
if c == nil {
return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.Id)
}
for volPath, id := range c.Volumes {
if _, exists := container.Volumes[volPath]; exists {
return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.Id)
}
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
return nil
}
container.Volumes[volPath] = id
}
}
if err := container.generateLXCConfig(); err != nil {
return err
}
params := []string{
"-n", container.Id,
"-f", container.lxcConfigPath(),
@@ -456,6 +496,7 @@ func (container *Container) Start() error {
// Init the lock
container.waitLock = make(chan struct{})
container.ToDisk()
go container.monitor()
return nil
@@ -530,16 +571,42 @@ func (container *Container) releaseNetwork() {
container.NetworkSettings = &NetworkSettings{}
}
// FIXME: replace this with a control socket within docker-init
func (container *Container) waitLxc() error {
for {
if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
return err
} else {
if !strings.Contains(string(output), "RUNNING") {
return nil
}
}
time.Sleep(500 * time.Millisecond)
}
return nil
}
func (container *Container) monitor() {
// Wait for the program to exit
Debugf("Waiting for process")
if err := container.cmd.Wait(); err != nil {
// Discard the error as any signals or non 0 returns will generate an error
Debugf("%s: Process: %s", container.Id, err)
// If the command does not exists, try to wait via lxc
if container.cmd == nil {
if err := container.waitLxc(); err != nil {
Debugf("%s: Process: %s", container.Id, err)
}
} else {
if err := container.cmd.Wait(); err != nil {
// Discard the error as any signals or non 0 returns will generate an error
Debugf("%s: Process: %s", container.Id, err)
}
}
Debugf("Process finished")
exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
var exitCode int = -1
if container.cmd != nil {
exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
}
// Cleanup
container.releaseNetwork()
@@ -588,7 +655,7 @@ func (container *Container) monitor() {
}
func (container *Container) kill() error {
if !container.State.Running || container.cmd == nil {
if !container.State.Running {
return nil
}
@@ -600,6 +667,9 @@ func (container *Container) kill() error {
// 2. Wait for the process to die, in last resort, try to kill the process directly
if err := container.WaitTimeout(10 * time.Second); err != nil {
if container.cmd == nil {
return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.Id)
}
log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.Id)
if err := container.cmd.Process.Kill(); err != nil {
return err
@@ -617,9 +687,6 @@ func (container *Container) Kill() error {
if !container.State.Running {
return nil
}
if container.State.Ghost {
return fmt.Errorf("Can't kill ghost container")
}
return container.kill()
}
@@ -629,9 +696,6 @@ func (container *Container) Stop(seconds int) error {
if !container.State.Running {
return nil
}
if container.State.Ghost {
return fmt.Errorf("Can't stop ghost container")
}
// 1. Send a SIGTERM
if output, err := exec.Command("lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
@@ -672,6 +736,14 @@ func (container *Container) ExportRw() (Archive, error) {
return Tar(container.rwPath(), Uncompressed)
}
func (container *Container) RwChecksum() (string, error) {
rwData, err := Tar(container.rwPath(), Xz)
if err != nil {
return "", err
}
return HashData(rwData)
}
func (container *Container) Export() (Archive, error) {
if err := container.EnsureMounted(); err != nil {
return nil, err
@@ -764,6 +836,22 @@ func (container *Container) RootfsPath() string {
return path.Join(container.root, "rootfs")
}
func (container *Container) GetVolumes() (map[string]string, error) {
ret := make(map[string]string)
for volPath, id := range container.Volumes {
volume, err := container.runtime.volumes.Get(id)
if err != nil {
return nil, err
}
root, err := volume.root()
if err != nil {
return nil, err
}
ret[volPath] = path.Join(root, "layer")
}
return ret, nil
}
func (container *Container) rwPath() string {
return path.Join(container.root, "rw")
}
+93 -27
View File
@@ -22,9 +22,8 @@ func TestIdFormat(t *testing.T) {
defer nuke(runtime)
container1, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).Id,
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
Memory: 33554432,
Image: GetTestImage(runtime).Id,
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
},
)
if err != nil {
@@ -50,7 +49,6 @@ func TestMultipleAttachRestart(t *testing.T) {
Image: GetTestImage(runtime).Id,
Cmd: []string{"/bin/sh", "-c",
"i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"},
Memory: 33554432,
},
)
if err != nil {
@@ -116,8 +114,8 @@ func TestMultipleAttachRestart(t *testing.T) {
if err := container.Start(); err != nil {
t.Fatal(err)
}
timeout := make(chan bool)
go func() {
setTimeout(t, "Timeout reading from the process", 3*time.Second, func() {
l1, err = bufio.NewReader(stdout1).ReadString('\n')
if err != nil {
t.Fatal(err)
@@ -139,15 +137,8 @@ func TestMultipleAttachRestart(t *testing.T) {
if strings.Trim(l3, " \r\n") != "hello" {
t.Fatalf("Unexpected output. Expected [%s], received [%s]", "hello", l3)
}
timeout <- false
}()
go func() {
time.Sleep(3 * time.Second)
timeout <- true
}()
if <-timeout {
t.Fatalf("Timeout reading from the process")
}
})
container.Wait()
}
func TestDiff(t *testing.T) {
@@ -193,7 +184,7 @@ func TestDiff(t *testing.T) {
if err != nil {
t.Error(err)
}
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image - diff", "")
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image - diff", "", nil)
if err != nil {
t.Error(err)
}
@@ -226,7 +217,7 @@ func TestDiff(t *testing.T) {
}
}
func TestCommitRun(t *testing.T) {
func TestCommitAutoRun(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
@@ -234,9 +225,8 @@ func TestCommitRun(t *testing.T) {
defer nuke(runtime)
container1, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).Id,
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
Memory: 33554432,
Image: GetTestImage(runtime).Id,
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
},
)
if err != nil {
@@ -258,7 +248,7 @@ func TestCommitRun(t *testing.T) {
if err != nil {
t.Error(err)
}
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image", "")
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image", "", &Config{Cmd: []string{"cat", "/world"}})
if err != nil {
t.Error(err)
}
@@ -267,9 +257,86 @@ func TestCommitRun(t *testing.T) {
container2, err := runtime.Create(
&Config{
Image: img.Id,
Memory: 33554432,
Cmd: []string{"cat", "/world"},
Image: img.Id,
},
)
if err != nil {
t.Fatal(err)
}
defer runtime.Destroy(container2)
stdout, err := container2.StdoutPipe()
if err != nil {
t.Fatal(err)
}
stderr, err := container2.StderrPipe()
if err != nil {
t.Fatal(err)
}
if err := container2.Start(); err != nil {
t.Fatal(err)
}
container2.Wait()
output, err := ioutil.ReadAll(stdout)
if err != nil {
t.Fatal(err)
}
output2, err := ioutil.ReadAll(stderr)
if err != nil {
t.Fatal(err)
}
if err := stdout.Close(); err != nil {
t.Fatal(err)
}
if err := stderr.Close(); err != nil {
t.Fatal(err)
}
if string(output) != "hello\n" {
t.Fatalf("Unexpected output. Expected %s, received: %s (err: %s)", "hello\n", output, output2)
}
}
func TestCommitRun(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
container1, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).Id,
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
},
)
if err != nil {
t.Fatal(err)
}
defer runtime.Destroy(container1)
if container1.State.Running {
t.Errorf("Container shouldn't be running")
}
if err := container1.Run(); err != nil {
t.Fatal(err)
}
if container1.State.Running {
t.Errorf("Container shouldn't be running")
}
rwTar, err := container1.ExportRw()
if err != nil {
t.Error(err)
}
img, err := runtime.graph.Create(rwTar, container1, "unit test commited image", "", nil)
if err != nil {
t.Error(err)
}
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
container2, err := runtime.Create(
&Config{
Image: img.Id,
Cmd: []string{"cat", "/world"},
},
)
if err != nil {
@@ -354,9 +421,8 @@ func TestRun(t *testing.T) {
defer nuke(runtime)
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).Id,
Memory: 33554432,
Cmd: []string{"ls", "-al"},
Image: GetTestImage(runtime).Id,
Cmd: []string{"ls", "-al"},
},
)
if err != nil {
+37 -7
View File
@@ -1,17 +1,22 @@
package main
import (
"fmt"
"io"
"log"
"net"
"os"
"os/exec"
"path"
"time"
)
const DOCKER_PATH = "/home/creack/dotcloud/docker/docker/docker"
var DOCKER_PATH string = path.Join(os.Getenv("DOCKERPATH"), "docker")
// WARNING: this crashTest will 1) crash your host, 2) remove all containers
func runDaemon() (*exec.Cmd, error) {
os.Remove("/var/run/docker.pid")
exec.Command("rm", "-rf", "/var/lib/docker/containers").Run()
cmd := exec.Command(DOCKER_PATH, "-d")
outPipe, err := cmd.StdoutPipe()
if err != nil {
@@ -38,19 +43,43 @@ func crashTest() error {
return err
}
var endpoint string
if ep := os.Getenv("TEST_ENDPOINT"); ep == "" {
endpoint = "192.168.56.1:7979"
} else {
endpoint = ep
}
c := make(chan bool)
var conn io.Writer
go func() {
conn, _ = net.Dial("tcp", endpoint)
c <- false
}()
go func() {
time.Sleep(2 * time.Second)
c <- true
}()
<-c
restartCount := 0
totalTestCount := 1
for {
daemon, err := runDaemon()
if err != nil {
return err
}
restartCount++
// time.Sleep(5000 * time.Millisecond)
var stop bool
go func() error {
stop = false
for i := 0; i < 100 && !stop; i++ {
for i := 0; i < 100 && !stop; {
func() error {
cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", "hello", "world")
log.Printf("%d", i)
cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
i++
totalTestCount++
outPipe, err := cmd.StdoutPipe()
if err != nil {
return err
@@ -62,9 +91,10 @@ func crashTest() error {
if err := cmd.Start(); err != nil {
return err
}
go func() {
io.Copy(os.Stdout, outPipe)
}()
if conn != nil {
go io.Copy(conn, outPipe)
}
// Expecting error, do not check
inPipe.Write([]byte("hello world!!!!!\n"))
go inPipe.Write([]byte("hello world!!!!!\n"))
+47 -9
View File
@@ -49,26 +49,39 @@ def docker(args, stdin=None):
def image_exists(img):
return docker(["inspect", img]).read().strip() != ""
def run_and_commit(img_in, cmd, stdin=None):
def image_config(img):
return json.loads(docker(["inspect", img]).read()).get("config", {})
def run_and_commit(img_in, cmd, stdin=None, author=None, run=None):
run_id = docker(["run"] + (["-i", "-a", "stdin"] if stdin else ["-d"]) + [img_in, "/bin/sh", "-c", cmd], stdin=stdin).read().rstrip()
print "---> Waiting for " + run_id
result=int(docker(["wait", run_id]).read().rstrip())
if result != 0:
print "!!! '{}' return non-zero exit code '{}'. Aborting.".format(cmd, result)
sys.exit(1)
return docker(["commit", run_id]).read().rstrip()
return docker(["commit"] + (["-author", author] if author else []) + (["-run", json.dumps(run)] if run is not None else []) + [run_id]).read().rstrip()
def insert(base, src, dst):
def insert(base, src, dst, author=None):
print "COPY {} to {} in {}".format(src, dst, base)
if dst == "":
raise Exception("Missing destination path")
stdin = file(src)
stdin.seek(0)
return run_and_commit(base, "cat > {0}; chmod +x {0}".format(dst), stdin=stdin)
return run_and_commit(base, "cat > {0}; chmod +x {0}".format(dst), stdin=stdin, author=author)
def add(base, src, dst, author=None):
print "PUSH to {} in {}".format(dst, base)
if src == ".":
tar = subprocess.Popen(["tar", "-c", "."], stdout=subprocess.PIPE).stdout
else:
tar = subprocess.Popen(["curl", src], stdout=subprocess.PIPE).stdout
if dst == "":
raise Exception("Missing argument to push")
return run_and_commit(base, "mkdir -p '{0}' && tar -C '{0}' -x".format(dst), stdin=tar, author=author)
def main():
base=""
maintainer=""
steps = []
try:
for line in sys.stdin.readlines():
@@ -77,22 +90,47 @@ def main():
if line == "" or line[0] == "#":
continue
op, param = line.split(" ", 1)
print op.upper() + " " + param
if op == "from":
print "FROM " + param
base = param
steps.append(base)
elif op == "maintainer":
maintainer = param
elif op == "run":
print "RUN " + param
result = run_and_commit(base, param)
result = run_and_commit(base, param, author=maintainer)
steps.append(result)
base = result
print "===> " + base
elif op == "copy":
src, dst = param.split(" ", 1)
result = insert(base, src, dst)
result = insert(base, src, dst, author=maintainer)
steps.append(result)
base = result
print "===> " + base
elif op == "add":
src, dst = param.split(" ", 1)
result = add(base, src, dst, author=maintainer)
steps.append(result)
base=result
print "===> " + base
elif op == "expose":
config = image_config(base)
if config.get("PortSpecs") is None:
config["PortSpecs"] = []
portspec = param.strip()
config["PortSpecs"].append(portspec)
result = run_and_commit(base, "# (nop) expose port {}".format(portspec), author=maintainer, run=config)
steps.append(result)
base=result
print "===> " + base
elif op == "cmd":
config = image_config(base)
cmd = list(json.loads(param))
config["Cmd"] = cmd
result = run_and_commit(base, "# (nop) set default command to '{}'".format(" ".join(cmd)), author=maintainer, run=config)
steps.append(result)
base=result
print "===> " + base
else:
print "Skipping uknown op " + op
except:
+5 -3
View File
@@ -1,11 +1,13 @@
# Start build from a know base image
maintainer Solomon Hykes <solomon@dotcloud.com>
from base:ubuntu-12.10
# Update ubuntu sources
run echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
run apt-get update
# Install system packages
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
# Insert files from the host (./myscript must be present in the current directory)
copy myscript /usr/local/bin/myscript
copy myscript /usr/local/bin/myscript
push /src
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
echo hello, world!
+61
View File
@@ -0,0 +1,61 @@
#!/bin/bash
set -e
# these should match the names found at http://www.debian.org/releases/
stableSuite='squeeze'
testingSuite='wheezy'
unstableSuite='sid'
# if suite is equal to this, it gets the "latest" tag
latestSuite="$testingSuite"
variant='minbase'
include='iproute,iputils-ping'
repo="$1"
suite="${2:-$latestSuite}"
mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
if [ ! "$repo" ]; then
echo >&2 "usage: $0 repo [suite [mirror]]"
echo >&2 " ie: $0 tianon/debian squeeze"
exit 1
fi
target="/tmp/docker-rootfs-debian-$suite-$$-$RANDOM"
cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
returnTo="$(pwd -P)"
set -x
# bootstrap
mkdir -p "$target"
sudo debootstrap --verbose --variant="$variant" --include="$include" "$suite" "$target" "$mirror"
cd "$target"
# create the image
img=$(sudo tar -c . | docker import -)
# tag suite
docker tag $img $repo $suite
if [ "$suite" = "$latestSuite" ]; then
# tag latest
docker tag $img $repo latest
fi
# test the image
docker run -i -t $repo:$suite echo success
# unstable's version numbers match testing (since it's mostly just a sandbox for testing), so it doesn't get a version number tag
if [ "$suite" != "$unstableSuite" -a "$suite" != 'unstable' ]; then
# tag the specific version
ver=$(docker run $repo:$suite cat /etc/debian_version)
docker tag $img $repo $ver
fi
# cleanup
cd "$returnTo"
sudo rm -rf "$target"
+4 -3
View File
@@ -28,6 +28,7 @@ func main() {
// FIXME: Switch d and D ? (to be more sshd like)
flDaemon := flag.Bool("d", false, "Daemon mode")
flDebug := flag.Bool("D", false, "Debug mode")
flAutoRestart := flag.Bool("r", false, "Restart previously running containers")
bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge")
pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
flag.Parse()
@@ -45,7 +46,7 @@ func main() {
flag.Usage()
return
}
if err := daemon(*pidfile); err != nil {
if err := daemon(*pidfile, *flAutoRestart); err != nil {
log.Fatal(err)
}
} else {
@@ -82,7 +83,7 @@ func removePidFile(pidfile string) {
}
}
func daemon(pidfile string) error {
func daemon(pidfile string, autoRestart bool) error {
if err := createPidFile(pidfile); err != nil {
log.Fatal(err)
}
@@ -97,7 +98,7 @@ func daemon(pidfile string) error {
os.Exit(0)
}()
service, err := docker.NewServer()
service, err := docker.NewServer(autoRestart)
if err != nil {
return err
}
+1
View File
@@ -51,6 +51,7 @@ docs:
cp sources/dotcloud.yml $(BUILDDIR)/html/
cp sources/CNAME $(BUILDDIR)/html/
cp sources/.nojekyll $(BUILDDIR)/html/
cp sources/nginx.conf $(BUILDDIR)/html/
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+2
View File
@@ -0,0 +1,2 @@
Sphinx==1.1.3
sphinxcontrib-httpdomain==1.1.8
@@ -9,3 +9,19 @@
Create a new image from a container's changes
-m="": Commit message
-author="": Author (eg. "John Hannibal Smith <hannibal@a-team.com>"
-run="": Config automatically applied when the image is run. "+`(ex: {"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')
Full -run example::
{"Hostname": "",
"User": "",
"Memory": 0,
"MemorySwap": 0,
"PortSpecs": ["22", "80", "443"],
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": ["FOO=BAR", "FOO2=BAR2"],
"Cmd": ["cat", "-e", "/etc/resolv.conf"],
"Dns": ["8.8.8.8", "8.8.4.4"]}
+3
View File
@@ -17,3 +17,6 @@
-p=[]: Map a network port to the container
-t=false: Allocate a pseudo-tty
-u="": Username or UID
-d=[]: Set custom dns servers for the container
-v=[]: Creates a new volumes and mount it at the specified path.
-volumes-from="": Mount all volumes from the given container.
+1 -1
View File
@@ -9,7 +9,7 @@ Commands
Contents:
.. toctree::
:maxdepth: 2
:maxdepth: 3
basics
workingwithrepository
+2 -2
View File
@@ -10,9 +10,9 @@ Building blocks
Images
------
An original container image. These are stored on disk and are comparable with what you normally expect from a stoppped virtual machine image. Images are stored (and retrieved from) repository
An original container image. These are stored on disk and are comparable with what you normally expect from a stopped virtual machine image. Images are stored (and retrieved from) repository
Images are stored on your local file system under /var/lib/docker/images
Images are stored on your local file system under /var/lib/docker/graph
.. _containers:
+1 -1
View File
@@ -25,7 +25,7 @@ import sys, os
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = []
extensions = ['sphinxcontrib.httpdomain']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -0,0 +1,53 @@
:title: Sharing data between 2 couchdb databases
:description: Sharing data between 2 couchdb databases
:keywords: docker, example, package installation, networking, couchdb, data volumes
.. _running_redis_service:
Create a redis service
======================
.. include:: example_header.inc
Here's an example of using data volumes to share the same data between 2 couchdb containers.
This could be used for hot upgrades, testing different versions of couchdb on the same data, etc.
Create first database
---------------------
Note that we're marking /var/lib/couchdb as a data volume.
.. code-block:: bash
COUCH1=$(docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03)
Add data to the first database
------------------------------
We're assuming your docker host is reachable at `localhost`. If not, replace `localhost` with the public IP of your docker host.
.. code-block:: bash
HOST=localhost
URL="http://$HOST:$(docker port $COUCH1 5984)/_utils/"
echo "Navigate to $URL in your browser, and use the couch interface to add data"
Create second database
----------------------
This time, we're requesting shared access to $COUCH1's volumes.
.. code-block:: bash
COUCH2=$(docker run -d -volumes-from $COUCH1) shykes/couchdb:2013-05-03)
Browse data on the second database
----------------------------------
.. code-block:: bash
HOST=localhost
URL="http://$HOST:$(docker port $COUCH2 5984)/_utils/"
echo "Navigate to $URL in your browser. You should see the same data as in the first database!"
Congratulations, you are running 2 Couchdb containers, completely isolated from each other *except* for their data.
+1
View File
@@ -18,3 +18,4 @@ Contents:
python_web_app
running_redis_service
running_ssh_service
couchdb_data_volumes
+1 -1
View File
@@ -49,7 +49,7 @@ Save the changed we just made in the container to a new image called "_/builds/g
WEB_WORKER=$(docker run -d -p 5000 $BUILD_IMG /usr/local/bin/runapp)
- **"docker run -d "** run a command in a new container. We pass "-d" so it runs as a daemon.
**"-p 5000"* the web app is going to listen on this port, so it must be mapped from the container to the host system.
- **"-p 5000"** the web app is going to listen on this port, so it must be mapped from the container to the host system.
- **"$BUILD_IMG"** is the image we want to run the command inside of.
- **/usr/local/bin/runapp** is the command which starts the web app.
+25 -19
View File
@@ -71,34 +71,40 @@
<h2>
<a name="installing-on-ubuntu-1204-and-1210" class="anchor" href="#installing-on-ubuntu-1204-and-1210"><span class="mini-icon mini-icon-link"></span>
</a>Installing on Ubuntu</h2>
<p><strong>Requirements</strong></p>
<ul>
<li>Ubuntu 12.04 (LTS) (64-bit)</li>
<li> or Ubuntu 12.10 (quantal) (64-bit)</li>
</ul>
<ol>
<li>
<p>Install dependencies:</p>
<p><strong>Install dependencies</strong></p>
The linux-image-extra package is only needed on standard Ubuntu EC2 AMIs in order to install the aufs kernel module.
<pre>sudo apt-get install linux-image-extra-`uname -r`</pre>
<div class="highlight">
<pre>sudo apt-get install lxc wget bsdtar curl</pre>
<pre>sudo apt-get install linux-image-extra-<span class="sb">`</span>uname -r<span class="sb">`</span></pre></div>
<p>The <code>linux-image-extra</code> package is needed on standard Ubuntu EC2 AMIs in order to install the aufs kernel module.</p>
</li>
<li>
<p>Install the latest docker binary:</p>
<p><strong>Install Docker</strong></p>
<p>Add the Ubuntu PPA (Personal Package Archive) sources to your apt sources list, update and install.</p>
<p>You may see some warnings that the GPG keys cannot be verified.</p>
<div class="highlight">
<pre>sudo sh -c "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >> /etc/apt/sources.list"</pre>
<pre>sudo apt-get update</pre>
<pre>sudo apt-get install lxc-docker</pre>
</div>
</li>
<li>
<p><strong>Run!</strong></p>
<div class="highlight">
<pre>wget http://get.docker.io/builds/<span class="k">$(</span>uname -s<span class="k">)</span>/<span class="k">$(</span>uname -m<span class="k">)</span>/docker-master.tgz</pre>
<pre>tar -xf docker-master.tgz</pre>
<pre>docker run -i -t ubuntu /bin/bash</pre>
</div>
</li>
<li>
<p>Run your first container!</p>
<div class="highlight"><pre><span class="nb">cd </span>docker-master</pre>
<pre>sudo ./docker run -i -t base /bin/bash</pre>
</div>
<p>Done!</p>
<p>Consider adding docker to your <code>PATH</code> for simplicity.</p>
</li>
Continue with the <a href="http://docs.docker.io/en/latest/examples/hello_world/">Hello world</a> example.
</ol>
</section>
@@ -117,7 +123,7 @@
vagrant and an Ubuntu virtual machine.</strong></p>
<ul>
<li><a href="http://docs.docker.io/en/latest/installation/macos/">Mac OS X and other linuxes</a></li>
<li><a href="http://docs.docker.io/en/latest/installation/vagrant/">Mac OS X and other linuxes</a></li>
<li><a href="http://docs.docker.io/en/latest/installation/windows/">Windows</a></li>
</ul>
+2
View File
@@ -15,6 +15,8 @@ This documentation has the following resources:
examples/index
contributing/index
commandline/index
registry/index
index/index
faq
+15
View File
@@ -0,0 +1,15 @@
:title: Docker Index documentation
:description: Documentation for docker Index
:keywords: docker, index, api
Index
=====
Contents:
.. toctree::
:maxdepth: 2
search
+38
View File
@@ -0,0 +1,38 @@
=======================
Docker Index Search API
=======================
Search
------
.. http:get:: /v1/search
Search the Index given a search term. It accepts :http:method:`get` only.
**Example request**:
.. sourcecode:: http
GET /v1/search?q=search_term HTTP/1.1
Host: example.com
Accept: application/json
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{"query":"search_term",
"num_results": 2,
"results" : [
{"name": "dotcloud/base", "description": "A base ubuntu64 image..."},
{"name": "base2", "description": "A base ubuntu64 image..."},
]
}
:query q: what you want to search for
:statuscode 200: no error
:statuscode 500: server error
+4 -3
View File
@@ -1,8 +1,9 @@
Amazon EC2
==========
Please note this is a community contributed installation path. The only 'official' installation is using the :ref:`ubuntu_linux` installation path. This version
may be out of date because it depends on some binaries to be updated and published
Please note this is a community contributed installation path. The only 'official' installation is using the
:ref:`ubuntu_linux` installation path. This version may sometimes be out of date.
Installation
------------
@@ -17,7 +18,7 @@ Docker can now be installed on Amazon EC2 with a single vagrant command. Vagrant
vagrant plugin install vagrant-aws
3. Get the docker sources, this will give you the latest Vagrantfile and puppet manifests.
3. Get the docker sources, this will give you the latest Vagrantfile.
::
+6
View File
@@ -3,6 +3,10 @@
Arch Linux
==========
Please note this is a community contributed installation path. The only 'official' installation is using the
:ref:`ubuntu_linux` installation path. This version may sometimes be out of date.
Installing on Arch Linux is not officially supported but can be handled via
either of the following AUR packages:
@@ -40,6 +44,7 @@ new kernel will be compiled and this can take quite a while.
yaourt -S lxc-docker-git
Starting Docker
---------------
@@ -52,6 +57,7 @@ There is a systemd service unit created for docker. To start the docker service
sudo systemctl start docker
To start on system boot:
::
+53
View File
@@ -0,0 +1,53 @@
.. _binaries:
Binaries
========
**Please note this project is currently under heavy development. It should not be used in production.**
Right now, the officially supported distributions are:
- Ubuntu 12.04 (precise LTS) (64-bit)
- Ubuntu 12.10 (quantal) (64-bit)
Install dependencies:
---------------------
::
sudo apt-get install lxc bsdtar
sudo apt-get install linux-image-extra-`uname -r`
The linux-image-extra package is needed on standard Ubuntu EC2 AMIs in order to install the aufs kernel module.
Install the docker binary:
::
wget http://get.docker.io/builds/Linux/x86_64/docker-master.tgz
tar -xf docker-master.tgz
sudo cp ./docker-master /usr/local/bin
Note: docker currently only supports 64-bit Linux hosts.
Run the docker daemon
---------------------
::
sudo docker -d &
Run your first container!
-------------------------
::
docker run -i -t ubuntu /bin/bash
Continue with the :ref:`hello_world` example.
+2 -1
View File
@@ -13,8 +13,9 @@ Contents:
:maxdepth: 1
ubuntulinux
binaries
archlinux
macos
vagrant
windows
amazon
upgrading
-66
View File
@@ -1,66 +0,0 @@
Mac OS X and other linux
========================
Please note this is a community contributed installation path. The only 'official' installation is using the :ref:`ubuntu_linux` installation path. This version
may be out of date because it depends on some binaries to be updated and published
Requirements
------------
We currently rely on some Ubuntu-linux specific packages, this will change in the future, but for now we provide a
streamlined path to install Virtualbox with a Ubuntu 12.10 image using Vagrant.
1. Install virtualbox from https://www.virtualbox.org/ (or use your package manager)
2. Install vagrant from http://www.vagrantup.com/ (or use your package manager)
3. Install git if you had not installed it before, check if it is installed by running
``git`` in a terminal window
We recommend having at least about 2Gb of free disk space and 2Gb RAM (or more).
Installation
------------
1. Fetch the docker sources
.. code-block:: bash
git clone https://github.com/dotcloud/docker.git
2. Run vagrant from the sources directory
.. code-block:: bash
vagrant up
Vagrant will:
* Download the Quantal64 base ubuntu virtual machine image from get.docker.io/
* Boot this image in virtualbox
Then it will use Puppet to perform an initial setup in this machine:
* Download & untar the most recent docker binary tarball to vagrant homedir.
* Debootstrap to /var/lib/docker/images/ubuntu.
* Install & run dockerd as service.
* Put docker in /usr/local/bin.
* Put latest Go toolchain in /usr/local/go.
You now have a Ubuntu Virtual Machine running with docker pre-installed.
To access the VM and use Docker, Run ``vagrant ssh`` from the same directory as where you ran
``vagrant up``. Vagrant will make sure to connect you to the correct VM.
.. code-block:: bash
vagrant ssh
Now you are in the VM, run docker
.. code-block:: bash
docker
Continue with the :ref:`hello_world` example.
+43 -38
View File
@@ -1,56 +1,61 @@
.. _ubuntu_linux:
Installing on Ubuntu Linux
==========================
Ubuntu Linux
============
**Please note this project is currently under heavy development. It should not be used in production.**
Installing on Ubuntu 12.04 and 12.10
Right now, the officially supported distributions are:
* Ubuntu 12.04 (precise LTS)
* Ubuntu 12.10 (quantal)
- Ubuntu 12.04 (precise LTS) (64-bit)
- Ubuntu 12.10 (quantal) (64-bit)
Install dependencies:
---------------------
Dependencies
------------
::
The linux-image-extra package is only needed on standard Ubuntu EC2 AMIs in order to install the aufs kernel module.
sudo apt-get install lxc bsdtar
sudo apt-get install linux-image-extra-`uname -r`
.. code-block:: bash
The linux-image-extra package is needed on standard Ubuntu EC2 AMIs in order to install the aufs kernel module.
Install the docker binary
-------------------------
::
wget http://get.docker.io/builds/Linux/x86_64/docker-master.tgz
tar -xf docker-master.tgz
sudo cp ./docker-master /usr/local/bin
Note: docker currently only supports 64-bit Linux hosts.
sudo apt-get install linux-image-extra-`uname -r`
Run the docker daemon
---------------------
Installation
------------
::
sudo docker -d &
Run your first container!
-------------------------
::
docker run -i -t ubuntu /bin/bash
Docker is available as a Ubuntu PPA (Personal Package Archive),
`hosted on launchpad <https://launchpad.net/~dotcloud/+archive/lxc-docker>`_
which makes installing Docker on Ubuntu very easy.
Check out more examples
-----------------------
Continue with the :ref:`hello_world` example.
Add the custom package sources to your apt sources list. Copy and paste the following lines at once.
.. code-block:: bash
sudo sh -c "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >> /etc/apt/sources.list"
Update your sources. You will see a warning that GPG signatures cannot be verified.
.. code-block:: bash
sudo apt-get update
Now install it, you will see another warning that the package cannot be authenticated. Confirm install.
.. code-block:: bash
sudo apt-get install lxc-docker
Verify it worked
.. code-block:: bash
docker
**Done!**, now continue with the :ref:`hello_world` example.
+2 -1
View File
@@ -3,7 +3,8 @@
Upgrading
============
We assume you are upgrading from within the operating system which runs your docker daemon.
These instructions are for upgrading your Docker binary for when you had a custom (non package manager) installation.
If you istalled docker using apt-get, use that to upgrade.
Get the latest docker binary:
+70
View File
@@ -0,0 +1,70 @@
.. _install_using_vagrant:
Using Vagrant
=============
Please note this is a community contributed installation path. The only 'official' installation is using the
:ref:`ubuntu_linux` installation path. This version may sometimes be out of date.
**Requirements:**
This guide will setup a new virtual machine with docker installed on your computer. This works on most operating
systems, including MacOX, Windows, Linux, FreeBSD and others. If you can install these and have at least 400Mb RAM
to spare you should be good.
Install Vagrant and Virtualbox
------------------------------
1. Install virtualbox from https://www.virtualbox.org/ (or use your package manager)
2. Install vagrant from http://www.vagrantup.com/ (or use your package manager)
3. Install git if you had not installed it before, check if it is installed by running
``git`` in a terminal window
Spin it up
----------
1. Fetch the docker sources (this includes the Vagrantfile for machine setup).
.. code-block:: bash
git clone https://github.com/dotcloud/docker.git
2. Run vagrant from the sources directory
.. code-block:: bash
vagrant up
Vagrant will:
* Download the 'official' Precise64 base ubuntu virtual machine image from vagrantup.com
* Boot this image in virtualbox
* Add the `Docker PPA sources <https://launchpad.net/~dotcloud/+archive/lxc-docker>`_ to /etc/apt/sources.lst
* Update your sources
* Install lxc-docker
You now have a Ubuntu Virtual Machine running with docker pre-installed.
Connect
-------
To access the VM and use Docker, Run ``vagrant ssh`` from the same directory as where you ran
``vagrant up``. Vagrant will connect you to the correct VM.
.. code-block:: bash
vagrant ssh
Run
-----
Now you are in the VM, run docker
.. code-block:: bash
docker
Continue with the :ref:`hello_world` example.
+2 -2
View File
@@ -3,8 +3,8 @@
:keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
Windows
=========
Windows (with Vagrant)
======================
Please note this is a community contributed installation path. The only 'official' installation is using the :ref:`ubuntu_linux` installation path. This version
may be out of date because it depends on some binaries to be updated and published
+6
View File
@@ -0,0 +1,6 @@
# rule to redirect original links created when hosted on github pages
rewrite ^/documentation/(.*).html http://docs.docker.io/en/latest/$1/ permanent;
# rewrite the stuff which was on the current page
rewrite ^/gettingstarted.html$ /gettingstarted/ permanent;
+468
View File
@@ -0,0 +1,468 @@
===================
Docker Registry API
===================
.. contents:: Table of Contents
1. The 3 roles
===============
1.1 Index
---------
The Index is responsible for centralizing information about:
- User accounts
- Checksums of the images
- Public namespaces
The Index has different components:
- Web UI
- Meta-data store (comments, stars, list public repositories)
- Authentication service
- Tokenization
The index is authoritative for those information.
We expect that there will be only one instance of the index, run and managed by dotCloud.
1.2 Registry
------------
- It stores the images and the graph for a set of repositories
- It does not have user accounts data
- It has no notion of user accounts or authorization
- It delegates authentication and authorization to the Index Auth service using tokens
- It supports different storage backends (S3, cloud files, local FS)
- It doesnt have a local database
- It will be open-sourced at some point
We expect that there will be multiple registries out there. To help to grasp the context, here are some examples of registries:
- **sponsor registry**: such a registry is provided by a third-party hosting infrastructure as a convenience for their customers and the docker community as a whole. Its costs are supported by the third party, but the management and operation of the registry are supported by dotCloud. It features read/write access, and delegates authentication and authorization to the Index.
- **mirror registry**: such a registry is provided by a third-party hosting infrastructure but is targeted at their customers only. Some mechanism (unspecified to date) ensures that public images are pulled from a sponsor registry to the mirror registry, to make sure that the customers of the third-party provider can “docker pull” those images locally.
- **vendor registry**: such a registry is provided by a software vendor, who wants to distribute docker images. It would be operated and managed by the vendor. Only users authorized by the vendor would be able to get write access. Some images would be public (accessible for anyone), others private (accessible only for authorized users). Authentication and authorization would be delegated to the Index. The goal of vendor registries is to let someone do “docker pull basho/riak1.3” and automatically push from the vendor registry (instead of a sponsor registry); i.e. get all the convenience of a sponsor registry, while retaining control on the asset distribution.
- **private registry**: such a registry is located behind a firewall, or protected by an additional security layer (HTTP authorization, SSL client-side certificates, IP address authorization...). The registry is operated by a private entity, outside of dotClouds control. It can optionally delegate additional authorization to the Index, but it is not mandatory.
.. note::
Mirror registries and private registries which do not use the Index dont even need to run the registry code. They can be implemented by any kind of transport implementing HTTP GET and PUT. Read-only registries can be powered by a simple static HTTP server.
.. note::
The latter implies that while HTTP is the protocol of choice for a registry, multiple schemes are possible (and in some cases, trivial):
- HTTP with GET (and PUT for read-write registries);
- local mount point;
- remote docker addressed through SSH.
The latter would only require two new commands in docker, e.g. “registryget” and “registryput”, wrapping access to the local filesystem (and optionally doing consistency checks). Authentication and authorization are then delegated to SSH (e.g. with public keys).
1.3 Docker
----------
On top of being a runtime for LXC, Docker is the Registry client. It supports:
- Push / Pull on the registry
- Client authentication on the Index
2. Workflow
===========
2.1 Pull
--------
.. image:: /static_files/docker_pull_chart.png
1. Contact the Index to know where I should download “samalba/busybox”
2. Index replies:
a. “samalba/busybox” is on Registry A
b. here are the checksums for “samalba/busybox” (for all layers)
c. token
3. Contact Registry A to receive the layers for “samalba/busybox” (all of them to the base image). Registry A is authoritative for “samalba/busybox” but keeps a copy of all inherited layers and serve them all from the same location.
4. registry contacts index to verify if token/user is allowed to download images
5. Index returns true/false lettings registry know if it should proceed or error out
6. Get the payload for all layers
Its possible to run docker pull https://<registry>/repositories/samalba/busybox. In this case, docker bypasses the Index. However the security is not guaranteed (in case Registry A is corrupted) because there wont be any checksum checks.
Currently registry redirects to s3 urls for downloads, going forward all downloads need to be streamed through the registry. The Registry will then abstract the calls to S3 by a top-level class which implements sub-classes for S3 and local storage.
Token is only returned when the 'X-Docker-Token' header is sent with request.
Basic Auth is required to pull private repos. Basic auth isn't required for pulling public repos, but if one is provided, it needs to be valid and for an active account.
API (pulling repository foo/bar):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1. (Docker -> Index) GET /v1/repositories/foo/bar/images
**Headers**:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
X-Docker-Token: true
**Action**:
(looking up the foo/bar in db and gets images and checksums for that repo (all if no tag is specified, if tag, only checksums for those tags) see part 4.4.1)
2. (Index -> Docker) HTTP 200 OK
**Headers**:
- Authorization: Token signature=123abc,repository=”foo/bar”,access=write
- X-Docker-Endpoints: registry.docker.io [, registry2.docker.io]
**Body**:
Jsonified checksums (see part 4.4.1)
3. (Docker -> Registry) GET /v1/repositories/foo/bar/tags/latest
**Headers**:
Authorization: Token signature=123abc,repository=”foo/bar”,access=write
4. (Registry -> Index) GET /v1/repositories/foo/bar/images
**Headers**:
Authorization: Token signature=123abc,repository=”foo/bar”,access=read
**Body**:
<ids and checksums in payload>
**Action**:
( Lookup token see if they have access to pull.)
If good:
HTTP 200 OK
Index will invalidate the token
If bad:
HTTP 401 Unauthorized
5. (Docker -> Registry) GET /v1/images/928374982374/ancestry
**Action**:
(for each image id returned in the registry, fetch /json + /layer)
.. note::
If someone makes a second request, then we will always give a new token, never reuse tokens.
2.2 Push
--------
.. image:: /static_files/docker_push_chart.png
1. Contact the index to allocate the repository name “samalba/busybox” (authentication required with user credentials)
2. If authentication works and namespace available, “samalba/busybox” is allocated and a temporary token is returned (namespace is marked as initialized in index)
3. Push the image on the registry (along with the token)
4. Registry A contacts the Index to verify the token (token must corresponds to the repository name)
5. Index validates the token. Registry A starts reading the stream pushed by docker and store the repository (with its images)
6. docker contacts the index to give checksums for upload images
.. note::
**Its possible not to use the Index at all!** In this case, a deployed version of the Registry is deployed to store and serve images. Those images are not authentified and the security is not guaranteed.
.. note::
**Index can be replaced!** For a private Registry deployed, a custom Index can be used to serve and validate token according to different policies.
Docker computes the checksums and submit them to the Index at the end of the push. When a repository name does not have checksums on the Index, it means that the push is in progress (since checksums are submitted at the end).
API (pushing repos foo/bar):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1. (Docker -> Index) PUT /v1/repositories/foo/bar/
**Headers**:
Authorization: Basic sdkjfskdjfhsdkjfh==
X-Docker-Token: true
**Action**::
- in index, we allocated a new repository, and set to initialized
**Body**::
(The body contains the list of images that are going to be pushed, with empty checksums. The checksums will be set at the end of the push)::
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}]
2. (Index -> Docker) 200 Created
**Headers**:
- WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=write
- X-Docker-Endpoints: registry.docker.io [, registry2.docker.io]
3. (Docker -> Registry) PUT /v1/images/98765432_parent/json
**Headers**:
Authorization: Token signature=123abc,repository=”foo/bar”,access=write
4. (Registry->Index) GET /v1/repositories/foo/bar/images
**Headers**:
Authorization: Token signature=123abc,repository=”foo/bar”,access=write
**Action**::
- Index:
will invalidate the token.
- Registry:
grants a session (if token is approved) and fetches the images id
5. (Docker -> Registry) PUT /v1/images/98765432_parent/json
**Headers**::
- Authorization: Token signature=123abc,repository=”foo/bar”,access=write
- Cookie: (Cookie provided by the Registry)
6. (Docker -> Registry) PUT /v1/images/98765432/json
**Headers**:
Cookie: (Cookie provided by the Registry)
7. (Docker -> Registry) PUT /v1/images/98765432_parent/layer
**Headers**:
Cookie: (Cookie provided by the Registry)
8. (Docker -> Registry) PUT /v1/images/98765432/layer
**Headers**:
X-Docker-Checksum: sha256:436745873465fdjkhdfjkgh
9. (Docker -> Registry) PUT /v1/repositories/foo/bar/tags/latest
**Headers**:
Cookie: (Cookie provided by the Registry)
**Body**:
“98765432”
10. (Docker -> Index) PUT /v1/repositories/foo/bar/images
**Headers**:
Authorization: Basic 123oislifjsldfj==
X-Docker-Endpoints: registry1.docker.io (no validation on this right now)
**Body**:
(The image, ids, tags and checksums)
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
“checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
**Return** HTTP 204
.. note::
If push fails and they need to start again, what happens in the index, there will already be a record for the namespace/name, but it will be initialized. Should we allow it, or mark as name already used? One edge case could be if someone pushes the same thing at the same time with two different shells.
If it's a retry on the Registry, Docker has a cookie (provided by the registry after token validation). So the Index wont have to provide a new token.
3. How to use the Registry in standalone mode
=============================================
The Index has two main purposes (along with its fancy social features):
- Resolve short names (to avoid passing absolute URLs all the time)
- username/projectname -> https://registry.docker.io/users/<username>/repositories/<projectname>/
- team/projectname -> https://registry.docker.io/team/<team>/repositories/<projectname>/
- Authenticate a user as a repos owner (for a central referenced repository)
3.1 Without an Index
--------------------
Using the Registry without the Index can be useful to store the images on a private network without having to rely on an external entity controlled by dotCloud.
In this case, the registry will be launched in a special mode (--standalone? --no-index?). In this mode, the only thing which changes is that Registry will never contact the Index to verify a token. It will be the Registry owner responsibility to authenticate the user who pushes (or even pulls) an image using any mechanism (HTTP auth, IP based, etc...).
In this scenario, the Registry is responsible for the security in case of data corruption since the checksums are not delivered by a trusted entity.
As hinted previously, a standalone registry can also be implemented by any HTTP server handling GET/PUT requests (or even only GET requests if no write access is necessary).
3.2 With an Index
-----------------
The Index data needed by the Registry are simple:
- Serve the checksums
- Provide and authorize a Token
In the scenario of a Registry running on a private network with the need of centralizing and authorizing, its easy to use a custom Index.
The only challenge will be to tell Docker to contact (and trust) this custom Index. Docker will be configurable at some point to use a specific Index, itll be the private entity responsibility (basically the organization who uses Docker in a private environment) to maintain the Index and the Dockers configuration among its consumers.
4. The API
==========
The first version of the api is available here: https://github.com/jpetazzo/docker/blob/acd51ecea8f5d3c02b00a08176171c59442df8b3/docs/images-repositories-push-pull.md
4.1 Images
----------
The format returned in the images is not defined here (for layer and json), basically because Registry stores exactly the same kind of information as Docker uses to manage them.
The format of ancestry is a line-separated list of image ids, in age order. I.e. the images parent is on the last line, the parent of the parent on the next-to-last line, etc.; if the image has no parent, the file is empty.
GET /v1/images/<image_id>/layer
PUT /v1/images/<image_id>/layer
GET /v1/images/<image_id>/json
PUT /v1/images/<image_id>/json
GET /v1/images/<image_id>/ancestry
PUT /v1/images/<image_id>/ancestry
4.2 Users
---------
4.2.1 Create a user (Index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
POST /v1/users
**Body**:
{"email": "sam@dotcloud.com", "password": "toto42", "username": "foobar"'}
**Validation**:
- **username** : min 4 character, max 30 characters, all lowercase no special characters.
- **password**: min 5 characters
**Valid**: return HTTP 200
Errors: HTTP 400 (we should create error codes for possible errors)
- invalid json
- missing field
- wrong format (username, password, email, etc)
- forbidden name
- name already exists
.. note::
A user account will be valid only if the email has been validated (a validation link is sent to the email address).
4.2.2 Update a user (Index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
PUT /v1/users/<username>
**Body**:
{"password": "toto"}
.. note::
We can also update email address, if they do, they will need to reverify their new email address.
4.2.3 Login (Index)
^^^^^^^^^^^^^^^^^^^
Does nothing else but asking for a user authentication. Can be used to validate credentials. HTTP Basic Auth for now, maybe change in future.
GET /v1/users
**Return**:
- Valid: HTTP 200
- Invalid login: HTTP 401
- Account inactive: HTTP 403 Account is not Active
4.3 Tags (Registry)
-------------------
The Registry does not know anything about users. Even though repositories are under usernames, its just a namespace for the registry. Allowing us to implement organizations or different namespaces per user later, without modifying the Registrys API.
4.3.1 Get all tags
^^^^^^^^^^^^^^^^^^
GET /v1/repositories/<namespace>/<repository_name>/tags
**Return**: HTTP 200
{
"latest": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
“0.1.1”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
}
4.3.2 Read the content of a tag (resolve the image id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
GET /v1/repositories/<namespace>/<repo_name>/tags/<tag>
**Return**:
"9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"
4.3.3 Delete a tag (registry)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
DELETE /v1/repositories/<namespace>/<repo_name>/tags/<tag>
4.4 Images (Index)
------------------
For the Index to “resolve” the repository name to a Registry location, it uses the X-Docker-Endpoints header. In other terms, this requests always add a “X-Docker-Endpoints” to indicate the location of the registry which hosts this repository.
4.4.1 Get the images
^^^^^^^^^^^^^^^^^^^^^
GET /v1/repositories/<namespace>/<repo_name>/images
**Return**: HTTP 200
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”, “checksum”: “md5:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
4.4.2 Add/update the images
^^^^^^^^^^^^^^^^^^^^^^^^^^^
You always add images, you never remove them.
PUT /v1/repositories/<namespace>/<repo_name>/images
**Body**:
[ {“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”, “checksum”: “sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”} ]
**Return** 204
5. Chaining Registries
======================
Its possible to chain Registries server for several reasons:
- Load balancing
- Delegate the next request to another server
When a Registry is a reference for a repository, it should host the entire images chain in order to avoid breaking the chain during the download.
The Index and Registry use this mechanism to redirect on one or the other.
Example with an image download:
On every request, a special header can be returned:
X-Docker-Endpoints: server1,server2
On the next request, the client will always pick a server from this list.
6. Authentication & Authorization
=================================
6.1 On the Index
-----------------
The Index supports both “Basic” and “Token” challenges. Usually when there is a “401 Unauthorized”, the Index replies this::
401 Unauthorized
WWW-Authenticate: Basic realm="auth required",Token
You have 3 options:
1. Provide user credentials and ask for a token
**Header**:
- Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
- X-Docker-Token: true
In this case, along with the 200 response, youll get a new token (if user auth is ok):
If authorization isn't correct you get a 401 response.
If account isn't active you will get a 403 response.
**Response**:
- 200 OK
- X-Docker-Token: Token signature=123abc,repository=”foo/bar”,access=read
2. Provide user credentials only
**Header**:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
3. Provide Token
**Header**:
Authorization: Token signature=123abc,repository=”foo/bar”,access=read
6.2 On the Registry
-------------------
The Registry only supports the Token challenge::
401 Unauthorized
WWW-Authenticate: Token
The only way is to provide a token on “401 Unauthorized” responses::
Authorization: Token signature=123abc,repository=”foo/bar”,access=read
Usually, the Registry provides a Cookie when a Token verification succeeded. Every time the Registry passes a Cookie, you have to pass it back the same cookie.::
200 OK
Set-Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=&timestamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="; Path=/; HttpOnly
Next request::
GET /(...)
Cookie: session="wD/J7LqL5ctqw8haL10vgfhrb2Q=?foo=UydiYXInCnAxCi4=&timestamp=RjEzNjYzMTQ5NDcuNDc0NjQzCi4="
+15
View File
@@ -0,0 +1,15 @@
:title: docker Registry documentation
:description: Documentation for docker Registry and Registry API
:keywords: docker, registry, api, index
Registry
========
Contents:
.. toctree::
:maxdepth: 2
api
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

+7 -3
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
@@ -13,8 +14,9 @@ import (
// A Graph is a store for versioned filesystem images and the relationship between them.
type Graph struct {
Root string
idIndex *TruncIndex
Root string
idIndex *TruncIndex
httpClient *http.Client
}
// NewGraph instantiates a new graph at the given root path in the filesystem.
@@ -84,13 +86,14 @@ func (graph *Graph) Get(name string) (*Image, error) {
}
// Create creates a new image and registers it in the graph.
func (graph *Graph) Create(layerData Archive, container *Container, comment, author string) (*Image, error) {
func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
img := &Image{
Id: GenerateId(),
Comment: comment,
Created: time.Now(),
DockerVersion: VERSION,
Author: author,
Config: config,
}
if container != nil {
img.Parent = container.Image
@@ -100,6 +103,7 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
if err := graph.Register(layerData, img); err != nil {
return nil, err
}
img.Checksum()
return img, nil
}
+6 -6
View File
@@ -62,7 +62,7 @@ func TestGraphCreate(t *testing.T) {
if err != nil {
t.Fatal(err)
}
image, err := graph.Create(archive, nil, "Testing", "")
image, err := graph.Create(archive, nil, "Testing", "", nil)
if err != nil {
t.Fatal(err)
}
@@ -122,7 +122,7 @@ func TestMount(t *testing.T) {
if err != nil {
t.Fatal(err)
}
image, err := graph.Create(archive, nil, "Testing", "")
image, err := graph.Create(archive, nil, "Testing", "", nil)
if err != nil {
t.Fatal(err)
}
@@ -166,7 +166,7 @@ func createTestImage(graph *Graph, t *testing.T) *Image {
if err != nil {
t.Fatal(err)
}
img, err := graph.Create(archive, nil, "Test image", "")
img, err := graph.Create(archive, nil, "Test image", "", nil)
if err != nil {
t.Fatal(err)
}
@@ -181,7 +181,7 @@ func TestDelete(t *testing.T) {
t.Fatal(err)
}
assertNImages(graph, t, 0)
img, err := graph.Create(archive, nil, "Bla bla", "")
img, err := graph.Create(archive, nil, "Bla bla", "", nil)
if err != nil {
t.Fatal(err)
}
@@ -192,11 +192,11 @@ func TestDelete(t *testing.T) {
assertNImages(graph, t, 0)
// Test 2 create (same name) / 1 delete
img1, err := graph.Create(archive, nil, "Testing", "")
img1, err := graph.Create(archive, nil, "Testing", "", nil)
if err != nil {
t.Fatal(err)
}
if _, err = graph.Create(archive, nil, "Testing", ""); err != nil {
if _, err = graph.Create(archive, nil, "Testing", "", nil); err != nil {
t.Fatal(err)
}
assertNImages(graph, t, 2)
-1
View File
@@ -1 +0,0 @@
This directory contains material helpful for hacking on docker.
+27
View File
@@ -0,0 +1,27 @@
This directory contains material helpful for hacking on docker.
make hack
=========
Set up an Ubuntu 13.04 virtual machine for developers including kernel 3.8
and buildbot. The environment is setup in a way that can be used through
the usual go workflow and/or the root Makefile. You can either edit on
your host, or inside the VM (using make ssh-dev) and run and test docker
inside the VM.
dependencies: vagrant, virtualbox packages and python package requests
Buildbot
~~~~~~~~
Buildbot is a continuous integration system designed to automate the
build/test cycle. By automatically rebuilding and testing the tree each time
something has changed, build problems are pinpointed quickly, before other
developers are inconvenienced by the failure.
When running 'make hack' at the docker root directory, it spawns a virtual
machine in the background running a buildbot instance and adds a git
post-commit hook that automatically run docker tests for you.
You can check your buildbot instance at http://192.168.33.21:8010/waterfall
+35
View File
@@ -0,0 +1,35 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
BOX_NAME = "ubuntu-dev"
BOX_URI = "http://cloud-images.ubuntu.com/raring/current/raring-server-cloudimg-vagrant-amd64-disk1.box"
VM_IP = "192.168.33.21"
USER = "vagrant"
GOPATH = "/data/docker"
DOCKER_PATH = "#{GOPATH}/src/github.com/dotcloud/docker"
CFG_PATH = "#{DOCKER_PATH}/hack/environment"
BUILDBOT_PATH = "/data/buildbot"
Vagrant::Config.run do |config|
# Setup virtual machine box
config.vm.box = BOX_NAME
config.vm.box_url = BOX_URI
config.vm.share_folder "v-data", DOCKER_PATH, "#{File.dirname(__FILE__)}/.."
config.vm.network :hostonly, VM_IP
# Stop if deployment has been done
config.vm.provision :shell, :inline => "[ ! -f /usr/bin/git ]"
# Touch for makefile
pkg_cmd = "touch #{DOCKER_PATH}; "
# Install docker dependencies
pkg_cmd << "export DEBIAN_FRONTEND=noninteractive; apt-get -qq update; " \
"apt-get install -q -y lxc bsdtar git golang make linux-image-extra-3.8.0-19-generic; " \
"chown -R #{USER}.#{USER} #{GOPATH}; " \
"install -m 0664 #{CFG_PATH}/bash_profile /home/#{USER}/.bash_profile"
config.vm.provision :shell, :inline => pkg_cmd
# Deploy buildbot CI
pkg_cmd = "apt-get install -q -y python-dev python-pip supervisor; " \
"pip install -r #{CFG_PATH}/requirements.txt; " \
"chown #{USER}.#{USER} /data; cd /data; " \
"#{CFG_PATH}/setup.sh #{USER} #{GOPATH} #{DOCKER_PATH} #{CFG_PATH} #{BUILDBOT_PATH}"
config.vm.provision :shell, :inline => pkg_cmd
end
+23
View File
@@ -0,0 +1,23 @@
# This will build a container capable of producing an official binary build of docker and
# uploading it to S3
maintainer Solomon Hykes <solomon@dotcloud.com>
from ubuntu:12.10
run apt-get update
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q s3cmd
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
# Packages required to checkout and build docker
run curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.0.3.linux-amd64.tar.gz
run tar -C /usr/local -xzf /go.tar.gz
run echo "export PATH=$PATH:/usr/local/go/bin" > /.bashrc
run echo "export PATH=$PATH:/usr/local/go/bin" > /.bash_profile
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q build-essential
# Packages required to build an ubuntu package
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q debhelper
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q autotools-dev
copy fake_initctl /usr/local/bin/initctl
run apt-get install -y -q devscripts
add . /src
run cp /src/dockerbuilder /usr/local/bin/ && chmod +x /usr/local/bin/dockerbuilder
run cp /src/s3cfg /.s3cfg
cmd ["dockerbuilder"]
+40
View File
@@ -0,0 +1,40 @@
#!/bin/sh
set -x
set -e
export PATH=$PATH:/usr/local/go/bin
PACKAGE=github.com/dotcloud/docker
if [ $# -gt 1 ]; then
echo "Usage: $0 [REVISION]"
exit 1
fi
export REVISION=$1
if [ -z "$AWS_ID" ]; then
echo "Warning: environment variable AWS_ID is not set. Won't upload to S3."
fi
if [ -z "$AWS_KEY" ]; then
echo "Warning: environment variable AWS_KEY is not set. Won't upload to S3."
fi
if [ -z "$GPG_KEY" ]; then
echo "Warning: environment variable GPG_KEY is not set. Ubuntu package upload will not succeed."
NO_UBUNTU=1
fi
rm -fr docker-release
git clone https://github.com/dotcloud/docker docker-release
cd docker-release
if [ -z "$REVISION" ]; then
make release
else
make release RELEASE_VERSION=$REVISION
fi
if [ -z "$NO_UBUNTU" ]; then
(cd packaging/ubuntu && make ubuntu)
fi
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
echo Whatever you say, man
+3
View File
@@ -0,0 +1,3 @@
[default]
access_key = $AWS_ID
secret_key = $AWS_KEY
+1
View File
@@ -0,0 +1 @@
Files used to setup the developer virtual machine
+19
View File
@@ -0,0 +1,19 @@
# ~/.bash_profile : executed by the command interpreter for login shells.
# if running bash
if [ -n "$BASH_VERSION" ]; then
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi
# set PATH so it includes user's private bin if it exists
[ -d "$HOME/bin" ] && PATH="$HOME/bin:$PATH"
docker=/data/docker/src/github.com/dotcloud/docker
[ -d $docker ] && cd $docker
export GOPATH=/data/docker
export PATH=$PATH:$GOPATH/bin
@@ -13,8 +13,8 @@ TEST_USER = 'buildbot' # Credential to authenticate build triggers
TEST_PWD = 'docker' # Credential to authenticate build triggers
BUILDER_NAME = 'docker'
BUILDPASSWORD = 'pass-docker' # Credential to authenticate buildworkers
DOCKER_PATH = '/data/docker'
GOPATH = '/data/docker'
DOCKER_PATH = '{0}/src/github.com/dotcloud/docker'.format(GOPATH)
c = BuildmasterConfig = {}
@@ -28,10 +28,7 @@ c['slavePortnum'] = PORT_MASTER
c['schedulers'] = [ForceScheduler(name='trigger',builderNames=[BUILDER_NAME])]
# Docker test command
test_cmd = """(
cd {0}/..; rm -rf docker-tmp; git clone docker docker-tmp;
cd docker-tmp; make test; exit_status=$?;
cd ..; rm -rf docker-tmp; exit $exit_status)""".format(DOCKER_PATH)
test_cmd = "GOPATH={0} make -C {1} test".format(GOPATH,DOCKER_PATH)
# Builder
factory = BuildFactory()
+6
View File
@@ -0,0 +1,6 @@
sqlalchemy<=0.7.9
sqlalchemy-migrate>=0.7.2
buildbot==0.8.7p1
buildbot_slave==0.8.7p1
nose==1.2.1
requests==1.1.0
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
# Setup of buildbot configuration. Package installation is being done by
# Vagrantfile
# Dependencies: buildbot, buildbot-slave, supervisor
USER=$1
GOPATH=$2
DOCKER_PATH=$3
CFG_PATH=$4
BUILDBOT_PATH=$5
SLAVE_NAME="buildworker"
SLAVE_SOCKET="localhost:9989"
BUILDBOT_PWD="pass-docker"
IP=$(sed -nE 's/VM_IP = "(.+)"/\1/p' ${DOCKER_PATH}/hack/Vagrantfile)
export PATH="/bin:sbin:/usr/bin:/usr/sbin:/usr/local/bin"
function run { su $USER -c "$1"; }
# Exit if buildbot has already been installed
[ -d "$BUILDBOT_PATH" ] && exit 0
# Setup buildbot
run "mkdir -p $BUILDBOT_PATH"
cd $BUILDBOT_PATH
run "buildbot create-master master"
run "cp $CFG_PATH/master.cfg master"
run "sed -i 's/localhost/$IP/' master/master.cfg"
run "sed -i -E 's#(GOPATH = ).+#\1\"$GOPATH\"#' master/master.cfg"
run "sed -i -E 's#(DOCKER_PATH = ).+#\1\"$DOCKER_PATH\"#' master/master.cfg"
run "buildslave create-slave slave $SLAVE_SOCKET $SLAVE_NAME $BUILDBOT_PWD"
# Allow buildbot subprocesses (docker tests) to properly run in containers,
# in particular with docker -u
run "sed -i 's/^umask = None/umask = 000/' slave/buildbot.tac"
# Setup supervisor
cp $CFG_PATH/buildbot.conf /etc/supervisor/conf.d/buildbot.conf
sed -i -E "s/^chmod=0700.+/chmod=0770\nchown=root:$USER/" /etc/supervisor/supervisord.conf
kill -HUP $(pgrep -f "/usr/bin/python /usr/bin/supervisord")
# Add git hook
cp $CFG_PATH/post-commit $DOCKER_PATH/.git/hooks
sed -i "s/localhost/$IP/" $DOCKER_PATH/.git/hooks/post-commit
+62
View File
@@ -2,6 +2,7 @@ package docker
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
@@ -24,6 +25,7 @@ type Image struct {
ContainerConfig Config `json:"container_config,omitempty"`
DockerVersion string `json:"docker_version,omitempty"`
Author string `json:"author,omitempty"`
Config *Config `json:"config,omitempty"`
graph *Graph
}
@@ -50,6 +52,7 @@ func LoadImage(root string) (*Image, error) {
} else if !stat.IsDir() {
return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.Id, layerPath(root))
}
return &img, nil
}
@@ -256,3 +259,62 @@ func (img *Image) layer() (string, error) {
}
return layerPath(root), nil
}
func (img *Image) Checksum() (string, error) {
root, err := img.root()
if err != nil {
return "", err
}
checksumDictPth := path.Join(root, "..", "..", "checksums")
checksums := new(map[string]string)
if checksumDict, err := ioutil.ReadFile(checksumDictPth); err == nil {
if err := json.Unmarshal(checksumDict, checksums); err != nil {
return "", err
}
if checksum, ok := (*checksums)[img.Id]; ok {
return checksum, nil
}
}
layer, err := img.layer()
if err != nil {
return "", err
}
jsonData, err := ioutil.ReadFile(jsonPath(root))
if err != nil {
return "", err
}
layerData, err := Tar(layer, Xz)
if err != nil {
return "", err
}
h := sha256.New()
if _, err := h.Write(jsonData); err != nil {
return "", err
}
if _, err := h.Write([]byte("\n")); err != nil {
return "", err
}
if _, err := io.Copy(h, layerData); err != nil {
return "", err
}
hash := "sha256:" + hex.EncodeToString(h.Sum(nil))
if *checksums == nil {
*checksums = map[string]string{}
}
(*checksums)[img.Id] = hash
checksumJson, err := json.Marshal(checksums)
if err != nil {
return hash, err
}
if err := ioutil.WriteFile(checksumDictPth, checksumJson, 0600); err != nil {
return hash, err
}
return hash, nil
}
+5 -1
View File
@@ -79,7 +79,11 @@ lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/sbin/init none bind,ro 0 0
# In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
{{if .Volumes}}
{{range $virtualPath, $realPath := .GetVolumes}}
lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,rw 0 0
{{end}}
{{end}}
# drop linux capabilities (apply mainly to the user root in the container)
lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config
+142 -14
View File
@@ -1,30 +1,158 @@
lxc-docker (0.3.0-1) precise; urgency=low
- Registry: Implement the new registry
- Documentation: new example: sharing data between 2 couchdb databases
- Runtime: Fix the command existance check
- Runtime: strings.Split may return an empty string on no match
- Runtime: Fix an index out of range crash if cgroup memory is not
- Documentation: Various improvments
- Vagrant: Use only one deb line in /etc/apt
-- dotCloud <ops@dotcloud.com> Fri, 5 May 2013 00:00:00 -0700
lxc-docker (0.2.2-1) precise; urgency=low
- Support for data volumes ('docker run -v=PATH')
- Share data volumes between containers ('docker run -volumes-from')
- Improved documentation
- Upgrade to Go 1.0.3
- Various upgrades to the dev environment for contributors
-- dotCloud <ops@dotcloud.com> Fri, 3 May 2013 00:00:00 -0700
lxc-docker (0.2.1-1) precise; urgency=low
- 'docker commit -run' bundles a layer with default runtime options: command, ports etc.
- Improve install process on Vagrant
- New Dockerfile operation: "maintainer"
- New Dockerfile operation: "expose"
- New Dockerfile operation: "cmd"
- Contrib script to build a Debian base layer
- 'docker -d -r': restart crashed containers at daemon startup
- Runtime: improve test coverage
-- dotCloud <ops@dotcloud.com> Wed, 1 May 2013 00:00:00 -0700
lxc-docker (0.2.0-1) precise; urgency=low
- Runtime: ghost containers can be killed and waited for
- Documentation: update install intructions
- Packaging: fix Vagrantfile
- Development: automate releasing binaries and ubuntu packages
- Add a changelog
- Various bugfixes
-- dotCloud <ops@dotcloud.com> Mon, 23 Apr 2013 00:00:00 -0700
lxc-docker (0.1.8-1) precise; urgency=low
- Dynamically detect cgroup capabilities
- Issue stability warning on kernels <3.8
- 'docker push' buffers on disk instead of memory
- Fix 'docker diff' for removed files
- Fix 'docker stop' for ghost containers
- Fix handling of pidfile
- Various bugfixes and stability improvements
-- dotCloud <ops@dotcloud.com> Mon, 22 Apr 2013 00:00:00 -0700
lxc-docker (0.1.7-1) precise; urgency=low
- Container ports are available on localhost
- 'docker ps' shows allocated TCP ports
- Contributors can run 'make hack' to start a continuous integration VM
- Streamline ubuntu packaging & uploading
- Various bugfixes and stability improvements
-- dotCloud <ops@dotcloud.com> Thu, 18 Apr 2013 00:00:00 -0700
lxc-docker (0.1.6-1) precise; urgency=low
Improvements [+], Updates [*], Bug fixes [-]:
+ Multiple improvements, updates and bug fixes
- Record the author an image with 'docker commit -author'
-- dotCloud <ops@dotcloud.com> Wed, 17 Apr 2013 20:43:43 -0700
-- dotCloud <ops@dotcloud.com> Wed, 17 Apr 2013 00:00:00 -0700
lxc-docker (0.1.4.1-1) precise; urgency=low
lxc-docker (0.1.5-1) precise; urgency=low
Improvements [+], Updates [*], Bug fixes [-]:
* Test PPA
- Disable standalone mode
- Use a custom DNS resolver with 'docker -d -dns'
- Detect ghost containers
- Improve diagnosis of missing system capabilities
- Allow disabling memory limits at compile time
- Add debian packaging
- Documentation: installing on Arch Linux
- Documentation: running Redis on docker
- Fixed lxc 0.9 compatibility
- Automatically load aufs module
- Various bugfixes and stability improvements
-- dotCloud <ops@dotcloud.com> Mon, 15 Apr 2013 12:14:50 -0700
-- dotCloud <ops@dotcloud.com> Wed, 17 Apr 2013 00:00:00 -0700
lxc-docker (0.1.4-1) precise; urgency=low
Improvements [+], Updates [*], Bug fixes [-]:
* Changed default bridge interface do 'docker0'
- Fix a race condition when running the port allocator
- Full support for TTY emulation
- Detach from a TTY session with the escape sequence `C-p C-q`
- Various bugfixes and stability improvements
- Minor UI improvements
- Automatically create our own bridge interface 'docker0'
-- dotCloud <ops@dotcloud.com> Fri, 12 Apr 2013 12:20:06 -0700
-- dotCloud <ops@dotcloud.com> Tue, 9 Apr 2013 00:00:00 -0700
lxc-docker (0.1.0-1) unstable; urgency=low
lxc-docker (0.1.3-1) precise; urgency=low
* Initial release
- Choose TCP frontend port with '-p :PORT'
- Layer format is versioned
- Major reliability improvements to the process manager
- Various bugfixes and stability improvements
-- dotCloud <ops@dotcloud.com> Mon, 25 Mar 2013 05:51:12 -0700
-- dotCloud <ops@dotcloud.com> Thu, 4 Apr 2013 00:00:00 -0700
lxc-docker (0.1.2-1) precise; urgency=low
- Set container hostname with 'docker run -h'
- Selective attach at run with 'docker run -a [stdin[,stdout[,stderr]]]'
- Various bugfixes and stability improvements
- UI polish
- Progress bar on push/pull
- Use XZ compression by default
- Make IP allocator lazy
-- dotCloud <ops@dotcloud.com> Wed, 3 Apr 2013 00:00:00 -0700
lxc-docker (0.1.1-1) precise; urgency=low
- Display shorthand IDs for convenience
- Stabilize process management
- Layers can include a commit message
- Simplified 'docker attach'
- Fixed support for re-attaching
- Various bugfixes and stability improvements
- Auto-download at run
- Auto-login on push
- Beefed up documentation
-- dotCloud <ops@dotcloud.com> Sun, 31 Mar 2013 00:00:00 -0700
lxc-docker (0.1.0-1) precise; urgency=low
- First release
- Implement registry in order to push/pull images
- TCP port allocation
- Fix termcaps on Linux
- Add documentation
- Add Vagrant support with Vagrantfile
- Add unit tests
- Add repository/tags to ease image management
- Improve the layer implementation
-- dotCloud <ops@dotcloud.com> Sat, 23 Mar 2013 00:00:00 -0700
+8 -4
View File
@@ -15,9 +15,12 @@ accessed adding the following line to /etc/apt/sources.list ::
Releasing a new package
~~~~~~~~~~~~~~~~~~~~~~~
The most relevant information to update is the changelog file:
The most relevant information to update is the packaging/ubuntu/changelog file:
Each new release should create a new first paragraph with new release version,
changes, and the maintainer information.
changes, and the maintainer information. The core of this paragraph is
located on CHANGELOG.md. Make sure to transcribe it and translate the formats
(eg: packaging/ubuntu/changelog uses 2 spaces for body change descriptions
instead of 1 space from CHANGELOG.md)
Assuming your PPA GPG signing key is on /media/usbdrive/docker.key, load it
into the GPG_KEY environment variable with::
@@ -28,8 +31,9 @@ into the GPG_KEY environment variable with::
After this is done and you are ready to upload the package to the PPA, you have
a couple of choices:
* Follow README.debian to generate the actual source packages and upload them
to the PPA
* Follow packaging/ubuntu/README.ubuntu to generate the actual source packages
and upload them to the PPA
* Let vagrant do all the work for you::
( cd docker/packaging/ubuntu; vagrant up )
-17
View File
@@ -1,17 +0,0 @@
node default {
exec {
"apt_update" :
command => "/usr/bin/apt-get update"
}
Package {
require => Exec['apt_update']
}
group { "puppet":
ensure => "present"
}
include "docker"
}
-99
View File
@@ -1,99 +0,0 @@
class virtualbox {
Package { ensure => "installed" }
# remove some files from the base vagrant image because they're old
file { "/home/vagrant/docker-master":
ensure => absent,
recurse => true,
force => true,
purge => true,
}
file { "/usr/local/bin/dockerd":
ensure => absent,
}
file { "/usr/local/bin/docker":
ensure => absent,
}
# Set up VirtualBox guest utils
package { "virtualbox-guest-utils": }
exec { "vbox-add" :
command => "/etc/init.d/vboxadd setup",
require => [
Package["virtualbox-guest-utils"],
Package["linux-headers-3.5.0-25-generic"], ],
}
}
class docker {
# update this with latest go binary dist
$go_url = "http://go.googlecode.com/files/go1.0.3.linux-amd64.tar.gz"
Package { ensure => "installed" }
package { ["lxc", "debootstrap", "wget", "bsdtar", "git",
"linux-image-3.5.0-25-generic",
"linux-image-extra-3.5.0-25-generic",
"linux-headers-3.5.0-25-generic"]: }
$ec2_version = file("/etc/ec2_version", "/dev/null")
$rax_version = inline_template("<%= %x{/usr/bin/xenstore-read vm-data/provider_data/provider} %>")
if ($ec2_version) {
$vagrant_user = "ubuntu"
$vagrant_home = "/home/ubuntu"
} elsif ($rax_version) {
$vagrant_user = "root"
$vagrant_home = "/root"
} else {
# virtualbox is the vagrant default, so it should be safe to assume
$vagrant_user = "vagrant"
$vagrant_home = "/home/vagrant"
include virtualbox
}
exec { "fetch-go":
require => Package["wget"],
command => "/usr/bin/wget -O - $go_url | /bin/tar xz -C /usr/local",
creates => "/usr/local/go/bin/go",
}
file { "/etc/init/dockerd.conf":
mode => 600,
owner => "root",
group => "root",
content => template("docker/dockerd.conf"),
}
file { "/opt/go":
owner => $vagrant_user,
group => $vagrant_user,
recurse => true,
}
file { "${vagrant_home}/.profile":
mode => 644,
owner => $vagrant_user,
group => $vagrant_user,
content => template("docker/profile"),
}
exec { "build-docker" :
cwd => "/opt/go/src/github.com/dotcloud/docker",
user => $vagrant_user,
environment => "GOPATH=/opt/go",
command => "/usr/local/go/bin/go get -v ./... && /usr/local/go/bin/go install ./docker",
creates => "/opt/go/bin/docker",
logoutput => "on_failure",
require => [ Exec["fetch-go"], File["/opt/go"] ],
}
service { "dockerd" :
ensure => "running",
start => "/sbin/initctl start dockerd",
stop => "/sbin/initctl stop dockerd",
require => [ Exec["build-docker"], File["/etc/init/dockerd.conf"] ],
name => "dockerd",
provider => "base"
}
}
@@ -1,12 +0,0 @@
description "Run dockerd"
stop on runlevel [!2345]
start on runlevel [3]
# if you want it to automatically restart if it crashes, leave the next line in
respawn
script
test -f /etc/default/locale && . /etc/default/locale || true
LANG=$LANG LC_ALL=$LANG /opt/go/bin/docker -d >> /var/log/dockerd 2>&1
end script
-30
View File
@@ -1,30 +0,0 @@
# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
# see /usr/share/doc/bash/examples/startup-files for examples.
# the files are located in the bash-doc package.
# the default umask is set in /etc/profile; for setting the umask
# for ssh logins, install and configure the libpam-umask package.
#umask 022
# if running bash
if [ -n "$BASH_VERSION" ]; then
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
PATH="$HOME/bin:$PATH"
fi
export GOPATH=/opt/go
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
docker=/opt/go/src/github.com/dotcloud/docker
if [ -d $docker ]; then
cd $docker
fi
+449 -217
View File
@@ -1,20 +1,20 @@
package docker
import (
"bytes"
"encoding/json"
"fmt"
"github.com/dotcloud/docker/auth"
"github.com/shin-/cookiejar"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"strings"
)
//FIXME: Set the endpoint in a conf file or via commandline
//const REGISTRY_ENDPOINT = "http://registry-creack.dotcloud.com/v1"
const REGISTRY_ENDPOINT = auth.REGISTRY_SERVER + "/v1"
const INDEX_ENDPOINT = auth.INDEX_SERVER + "/v1"
// Build an Image object from raw json data
func NewImgJson(src []byte) (*Image, error) {
@@ -28,34 +28,23 @@ func NewImgJson(src []byte) (*Image, error) {
return ret, nil
}
// Build an Image object list from a raw json data
// FIXME: Do this in "stream" mode
func NewMultipleImgJson(src []byte) ([]*Image, error) {
ret := []*Image{}
dec := json.NewDecoder(strings.NewReader(string(src)))
for {
m := &Image{}
if err := dec.Decode(m); err == io.EOF {
break
} else if err != nil {
return nil, err
}
ret = append(ret, m)
func doWithCookies(c *http.Client, req *http.Request) (*http.Response, error) {
for _, cookie := range c.Jar.Cookies(req.URL) {
req.AddCookie(cookie)
}
return ret, nil
return c.Do(req)
}
// Retrieve the history of a given image from the Registry.
// Return a list of the parent's json (requested image included)
func (graph *Graph) getRemoteHistory(imgId string, authConfig *auth.AuthConfig) ([]*Image, error) {
client := &http.Client{}
func (graph *Graph) getRemoteHistory(imgId, registry string, token []string) ([]string, error) {
client := graph.getHttpClient()
req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/history", nil)
req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/ancestry", nil)
if err != nil {
return nil, err
}
req.SetBasicAuth(authConfig.Username, authConfig.Password)
req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
res, err := client.Do(req)
if err != nil || res.StatusCode != 200 {
if res != nil {
@@ -70,41 +59,83 @@ func (graph *Graph) getRemoteHistory(imgId string, authConfig *auth.AuthConfig)
return nil, fmt.Errorf("Error while reading the http response: %s\n", err)
}
history, err := NewMultipleImgJson(jsonString)
if err != nil {
return nil, fmt.Errorf("Error while parsing the json: %s\n", err)
Debugf("Ancestry: %s", jsonString)
history := new([]string)
if err := json.Unmarshal(jsonString, history); err != nil {
return nil, err
}
return history, nil
return *history, nil
}
func (graph *Graph) getHttpClient() *http.Client {
if graph.httpClient == nil {
graph.httpClient = new(http.Client)
graph.httpClient.Jar = cookiejar.NewCookieJar()
}
return graph.httpClient
}
// Check if an image exists in the Registry
func (graph *Graph) LookupRemoteImage(imgId string, authConfig *auth.AuthConfig) bool {
func (graph *Graph) LookupRemoteImage(imgId, registry string, authConfig *auth.AuthConfig) bool {
rt := &http.Transport{Proxy: http.ProxyFromEnvironment}
req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/json", nil)
req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/json", nil)
if err != nil {
return false
}
req.SetBasicAuth(authConfig.Username, authConfig.Password)
res, err := rt.RoundTrip(req)
if err != nil || res.StatusCode != 307 {
return false
return err == nil && res.StatusCode == 307
}
func (graph *Graph) getImagesInRepository(repository string, authConfig *auth.AuthConfig) ([]map[string]string, error) {
u := INDEX_ENDPOINT + "/repositories/" + repository + "/images"
req, err := http.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
return res.StatusCode == 307
if authConfig != nil && len(authConfig.Username) > 0 {
req.SetBasicAuth(authConfig.Username, authConfig.Password)
}
res, err := graph.getHttpClient().Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
// Repository doesn't exist yet
if res.StatusCode == 404 {
return nil, nil
}
jsonData, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
imageList := []map[string]string{}
err = json.Unmarshal(jsonData, &imageList)
if err != nil {
Debugf("Body: %s (%s)\n", res.Body, u)
return nil, err
}
return imageList, nil
}
// Retrieve an image from the Registry.
// Returns the Image object as well as the layer as an Archive (io.Reader)
func (graph *Graph) getRemoteImage(stdout io.Writer, imgId string, authConfig *auth.AuthConfig) (*Image, Archive, error) {
client := &http.Client{}
func (graph *Graph) getRemoteImage(stdout io.Writer, imgId, registry string, token []string) (*Image, Archive, error) {
client := graph.getHttpClient()
fmt.Fprintf(stdout, "Pulling %s metadata\r\n", imgId)
// Get the Json
req, err := http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/json", nil)
req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/json", nil)
if err != nil {
return nil, nil, fmt.Errorf("Failed to download json: %s", err)
}
req.SetBasicAuth(authConfig.Username, authConfig.Password)
req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
res, err := client.Do(req)
if err != nil {
return nil, nil, fmt.Errorf("Failed to download json: %s", err)
@@ -127,11 +158,11 @@ func (graph *Graph) getRemoteImage(stdout io.Writer, imgId string, authConfig *a
// Get the layer
fmt.Fprintf(stdout, "Pulling %s fs layer\r\n", imgId)
req, err = http.NewRequest("GET", REGISTRY_ENDPOINT+"/images/"+imgId+"/layer", nil)
req, err = http.NewRequest("GET", registry+"/images/"+imgId+"/layer", nil)
if err != nil {
return nil, nil, fmt.Errorf("Error while getting from the server: %s\n", err)
}
req.SetBasicAuth(authConfig.Username, authConfig.Password)
req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
res, err = client.Do(req)
if err != nil {
return nil, nil, err
@@ -139,16 +170,87 @@ func (graph *Graph) getRemoteImage(stdout io.Writer, imgId string, authConfig *a
return img, ProgressReader(res.Body, int(res.ContentLength), stdout, "Downloading %v/%v (%v)"), nil
}
func (graph *Graph) PullImage(stdout io.Writer, imgId string, authConfig *auth.AuthConfig) error {
history, err := graph.getRemoteHistory(imgId, authConfig)
func (graph *Graph) getRemoteTags(stdout io.Writer, registries []string, repository string, token []string) (map[string]string, error) {
client := graph.getHttpClient()
if strings.Count(repository, "/") == 0 {
// This will be removed once the Registry supports auto-resolution on
// the "library" namespace
repository = "library/" + repository
}
for _, host := range registries {
endpoint := fmt.Sprintf("https://%s/v1/repositories/%s/tags", host, repository)
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
res, err := client.Do(req)
defer res.Body.Close()
Debugf("Got status code %d from %s", res.StatusCode, endpoint)
if err != nil || (res.StatusCode != 200 && res.StatusCode != 404) {
continue
} else if res.StatusCode == 404 {
return nil, fmt.Errorf("Repository not found")
}
result := new(map[string]string)
rawJson, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
if err = json.Unmarshal(rawJson, result); err != nil {
return nil, err
}
return *result, nil
}
return nil, fmt.Errorf("Could not reach any registry endpoint")
}
func (graph *Graph) getImageForTag(stdout io.Writer, tag, remote, registry string, token []string) (string, error) {
client := graph.getHttpClient()
registryEndpoint := "https://" + registry + "/v1"
repositoryTarget := registryEndpoint + "/repositories/" + remote + "/tags/" + tag
req, err := http.NewRequest("GET", repositoryTarget, nil)
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
res, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("Error while retrieving repository info: %v", err)
}
defer res.Body.Close()
if res.StatusCode == 403 {
return "", fmt.Errorf("You aren't authorized to access this resource")
} else if res.StatusCode != 200 {
return "", fmt.Errorf("HTTP code: %d", res.StatusCode)
}
var imgId string
rawJson, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", err
}
if err = json.Unmarshal(rawJson, &imgId); err != nil {
return "", err
}
return imgId, nil
}
func (graph *Graph) PullImage(stdout io.Writer, imgId, registry string, token []string) error {
history, err := graph.getRemoteHistory(imgId, registry, token)
if err != nil {
return err
}
// FIXME: Try to stream the images?
// FIXME: Lunch the getRemoteImage() in goroutines
for _, j := range history {
if !graph.Exists(j.Id) {
img, layer, err := graph.getRemoteImage(stdout, j.Id, authConfig)
// FIXME: Launch the getRemoteImage() in goroutines
for _, id := range history {
if !graph.Exists(id) {
img, layer, err := graph.getRemoteImage(stdout, id, registry, token)
if err != nil {
// FIXME: Keep goging in case of error?
return err
@@ -161,165 +263,195 @@ func (graph *Graph) PullImage(stdout io.Writer, imgId string, authConfig *auth.A
return nil
}
// FIXME: Handle the askedTag parameter
func (graph *Graph) PullRepository(stdout io.Writer, remote, askedTag string, repositories *TagStore, authConfig *auth.AuthConfig) error {
client := &http.Client{}
client := graph.getHttpClient()
fmt.Fprintf(stdout, "Pulling repository %s\r\n", remote)
var repositoryTarget string
// If we are asking for 'root' repository, lookup on the Library's registry
if strings.Index(remote, "/") == -1 {
repositoryTarget = REGISTRY_ENDPOINT + "/library/" + remote
} else {
repositoryTarget = REGISTRY_ENDPOINT + "/users/" + remote
}
fmt.Fprintf(stdout, "Pulling repository %s from %s\r\n", remote, INDEX_ENDPOINT)
repositoryTarget := INDEX_ENDPOINT + "/repositories/" + remote + "/images"
req, err := http.NewRequest("GET", repositoryTarget, nil)
if err != nil {
return err
}
req.SetBasicAuth(authConfig.Username, authConfig.Password)
if authConfig != nil && len(authConfig.Username) > 0 {
req.SetBasicAuth(authConfig.Username, authConfig.Password)
}
req.Header.Set("X-Docker-Token", "true")
res, err := client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode == 401 {
return fmt.Errorf("Please login first (HTTP code %d)", res.StatusCode)
}
// TODO: Right now we're ignoring checksums in the response body.
// In the future, we need to use them to check image validity.
if res.StatusCode != 200 {
return fmt.Errorf("HTTP code: %d", res.StatusCode)
}
rawJson, err := ioutil.ReadAll(res.Body)
var token, endpoints []string
if res.Header.Get("X-Docker-Token") != "" {
token = res.Header["X-Docker-Token"]
}
if res.Header.Get("X-Docker-Endpoints") != "" {
endpoints = res.Header["X-Docker-Endpoints"]
} else {
return fmt.Errorf("Index response didn't contain any endpoints")
}
var tagsList map[string]string
if askedTag == "" {
tagsList, err = graph.getRemoteTags(stdout, endpoints, remote, token)
if err != nil {
return err
}
} else {
tagsList = map[string]string{askedTag: ""}
}
for askedTag, imgId := range tagsList {
fmt.Fprintf(stdout, "Resolving tag \"%s:%s\" from %s\n", remote, askedTag, endpoints)
success := false
for _, registry := range endpoints {
if imgId == "" {
imgId, err = graph.getImageForTag(stdout, askedTag, remote, registry, token)
if err != nil {
fmt.Fprintf(stdout, "Error while retrieving image for tag: %v (%v) ; "+
"checking next endpoint", askedTag, err)
continue
}
}
if err := graph.PullImage(stdout, imgId, "https://"+registry+"/v1", token); err != nil {
return err
}
if err = repositories.Set(remote, askedTag, imgId, true); err != nil {
return err
}
success = true
}
if !success {
return fmt.Errorf("Could not find repository on any of the indexed registries.")
}
}
if err = repositories.Save(); err != nil {
return err
}
return nil
}
func pushImageRec(graph *Graph, stdout io.Writer, img *Image, registry string, token []string) error {
if parent, err := img.GetParent(); err != nil {
return err
} else if parent != nil {
if err := pushImageRec(graph, stdout, parent, registry, token); err != nil {
return err
}
}
client := graph.getHttpClient()
jsonRaw, err := ioutil.ReadFile(path.Join(graph.Root, img.Id, "json"))
if err != nil {
return fmt.Errorf("Error while retreiving the path for {%s}: %s", img.Id, err)
}
fmt.Fprintf(stdout, "Pushing %s metadata\r\n", img.Id)
// FIXME: try json with UTF8
jsonData := strings.NewReader(string(jsonRaw))
req, err := http.NewRequest("PUT", registry+"/images/"+img.Id+"/json", jsonData)
if err != nil {
return err
}
t := map[string]string{}
if err = json.Unmarshal(rawJson, &t); err != nil {
req.Header.Add("Content-type", "application/json")
req.Header.Set("Authorization", "Token "+strings.Join(token, ","))
checksum, err := img.Checksum()
if err != nil {
return fmt.Errorf("Error while retrieving checksum for %s: %v", img.Id, err)
}
req.Header.Set("X-Docker-Checksum", checksum)
res, err := doWithCookies(client, req)
if err != nil {
return fmt.Errorf("Failed to upload metadata: %s", err)
}
defer res.Body.Close()
if len(res.Cookies()) > 0 {
client.Jar.SetCookies(req.URL, res.Cookies())
}
if res.StatusCode != 200 {
errBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("HTTP code %d while uploading metadata and error when"+
" trying to parse response body: %v", res.StatusCode, err)
}
var jsonBody map[string]string
if err := json.Unmarshal(errBody, &jsonBody); err != nil {
errBody = []byte(err.Error())
} else if jsonBody["error"] == "Image already exists" {
fmt.Fprintf(stdout, "Image %v already uploaded ; skipping\n", img.Id)
return nil
}
return fmt.Errorf("HTTP code %d while uploading metadata: %s", res.StatusCode, errBody)
}
fmt.Fprintf(stdout, "Pushing %s fs layer\r\n", img.Id)
layerData, err := graph.TempLayerArchive(img.Id, Xz, stdout)
if err != nil {
return fmt.Errorf("Failed to generate layer archive: %s", err)
}
req3, err := http.NewRequest("PUT", registry+"/images/"+img.Id+"/layer",
ProgressReader(layerData, -1, stdout, ""))
if err != nil {
return err
}
for tag, rev := range t {
fmt.Fprintf(stdout, "Pulling tag %s:%s\r\n", remote, tag)
if err = graph.PullImage(stdout, rev, authConfig); err != nil {
return err
}
if err = repositories.Set(remote, tag, rev, true); err != nil {
return err
}
req3.ContentLength = -1
req3.TransferEncoding = []string{"chunked"}
req3.Header.Set("Authorization", "Token "+strings.Join(token, ","))
res3, err := doWithCookies(client, req3)
if err != nil {
return fmt.Errorf("Failed to upload layer: %s", err)
}
if err = repositories.Save(); err != nil {
return err
res3.Body.Close()
if res3.StatusCode != 200 {
return fmt.Errorf("Received HTTP code %d while uploading layer", res3.StatusCode)
}
return nil
}
// Push a local image to the registry with its history if needed
func (graph *Graph) PushImage(stdout io.Writer, imgOrig *Image, authConfig *auth.AuthConfig) error {
client := &http.Client{}
// FIXME: Factorize the code
// FIXME: Do the puts in goroutines
if err := imgOrig.WalkHistory(func(img *Image) error {
jsonRaw, err := ioutil.ReadFile(path.Join(graph.Root, img.Id, "json"))
if err != nil {
return fmt.Errorf("Error while retreiving the path for {%s}: %s", img.Id, err)
}
fmt.Fprintf(stdout, "Pushing %s metadata\r\n", img.Id)
// FIXME: try json with UTF8
jsonData := strings.NewReader(string(jsonRaw))
req, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/images/"+img.Id+"/json", jsonData)
if err != nil {
return err
}
req.Header.Add("Content-type", "application/json")
req.SetBasicAuth(authConfig.Username, authConfig.Password)
res, err := client.Do(req)
if err != nil {
return fmt.Errorf("Failed to upload metadata: %s", err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
switch res.StatusCode {
case 204:
// Case where the image is already on the Registry
// FIXME: Do not be silent?
return nil
default:
errBody, err := ioutil.ReadAll(res.Body)
if err != nil {
errBody = []byte(err.Error())
}
return fmt.Errorf("HTTP code %d while uploading metadata: %s", res.StatusCode, errBody)
}
}
fmt.Fprintf(stdout, "Pushing %s fs layer\r\n", img.Id)
req2, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/images/"+img.Id+"/layer", nil)
req2.SetBasicAuth(authConfig.Username, authConfig.Password)
res2, err := client.Do(req2)
if err != nil {
return fmt.Errorf("Registry returned error: %s", err)
}
res2.Body.Close()
if res2.StatusCode != 307 {
return fmt.Errorf("Registry returned unexpected HTTP status code %d, expected 307", res2.StatusCode)
}
url, err := res2.Location()
if err != nil || url == nil {
return fmt.Errorf("Failed to retrieve layer upload location: %s", err)
}
// FIXME: stream the archive directly to the registry instead of buffering it on disk. This requires either:
// a) Implementing S3's proprietary streaming logic, or
// b) Stream directly to the registry instead of S3.
// I prefer option b. because it doesn't lock us into a proprietary cloud service.
tmpLayer, err := graph.TempLayerArchive(img.Id, Xz, stdout)
if err != nil {
return err
}
defer os.Remove(tmpLayer.Name())
req3, err := http.NewRequest("PUT", url.String(), ProgressReader(tmpLayer, int(tmpLayer.Size), stdout, "Uploading %v/%v (%v)"))
if err != nil {
return err
}
req3.ContentLength = int64(tmpLayer.Size)
req3.TransferEncoding = []string{"none"}
res3, err := client.Do(req3)
if err != nil {
return fmt.Errorf("Failed to upload layer: %s", err)
}
res3.Body.Close()
if res3.StatusCode != 200 {
return fmt.Errorf("Received HTTP code %d while uploading layer", res3.StatusCode)
}
return nil
}); err != nil {
return err
}
return nil
func (graph *Graph) PushImage(stdout io.Writer, imgOrig *Image, registry string, token []string) error {
registry = "https://" + registry + "/v1"
return pushImageRec(graph, stdout, imgOrig, registry, token)
}
// push a tag on the registry.
// Remote has the format '<user>/<repo>
func (graph *Graph) pushTag(remote, revision, tag string, authConfig *auth.AuthConfig) error {
// Keep this for backward compatibility
if tag == "" {
tag = "lastest"
}
func (graph *Graph) pushTag(remote, revision, tag, registry string, token []string) error {
// "jsonify" the string
revision = "\"" + revision + "\""
registry = "https://" + registry + "/v1"
Debugf("Pushing tags for rev [%s] on {%s}\n", revision, REGISTRY_ENDPOINT+"/users/"+remote+"/"+tag)
Debugf("Pushing tags for rev [%s] on {%s}\n", revision, registry+"/users/"+remote+"/"+tag)
client := &http.Client{}
req, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/users/"+remote+"/"+tag, strings.NewReader(revision))
client := graph.getHttpClient()
req, err := http.NewRequest("PUT", registry+"/repositories/"+remote+"/tags/"+tag, strings.NewReader(revision))
if err != nil {
return err
}
req.Header.Add("Content-type", "application/json")
req.SetBasicAuth(authConfig.Username, authConfig.Password)
res, err := client.Do(req)
req.Header.Set("Authorization", "Token "+strings.Join(token, ","))
req.ContentLength = int64(len(revision))
res, err := doWithCookies(client, req)
if err != nil {
return err
}
@@ -327,62 +459,25 @@ func (graph *Graph) pushTag(remote, revision, tag string, authConfig *auth.AuthC
if res.StatusCode != 200 && res.StatusCode != 201 {
return fmt.Errorf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote)
}
Debugf("Result of push tag: %d\n", res.StatusCode)
switch res.StatusCode {
default:
return fmt.Errorf("Error %d\n", res.StatusCode)
case 200:
case 201:
}
return nil
}
func (graph *Graph) LookupRemoteRepository(remote string, authConfig *auth.AuthConfig) bool {
rt := &http.Transport{Proxy: http.ProxyFromEnvironment}
var repositoryTarget string
// If we are asking for 'root' repository, lookup on the Library's registry
if strings.Index(remote, "/") == -1 {
repositoryTarget = REGISTRY_ENDPOINT + "/library/" + remote + "/lookup"
} else {
repositoryTarget = REGISTRY_ENDPOINT + "/users/" + remote + "/lookup"
}
Debugf("Checking for permissions on: %s", repositoryTarget)
req, err := http.NewRequest("PUT", repositoryTarget, strings.NewReader("\"\""))
if err != nil {
Debugf("%s\n", err)
return false
}
req.SetBasicAuth(authConfig.Username, authConfig.Password)
req.Header.Add("Content-type", "application/json")
res, err := rt.RoundTrip(req)
if err != nil || res.StatusCode != 404 {
errBody, err := ioutil.ReadAll(res.Body)
if err != nil {
errBody = []byte(err.Error())
}
Debugf("Lookup status code: %d (body: %s)", res.StatusCode, errBody)
return false
}
return true
}
// FIXME: this should really be PushTag
func (graph *Graph) pushPrimitive(stdout io.Writer, remote, tag, imgId string, authConfig *auth.AuthConfig) error {
func (graph *Graph) pushPrimitive(stdout io.Writer, remote, tag, imgId, registry string, token []string) error {
// Check if the local impage exists
img, err := graph.Get(imgId)
if err != nil {
fmt.Fprintf(stdout, "Skipping tag %s:%s: %s does not exist\r\n", remote, tag, imgId)
return nil
}
fmt.Fprintf(stdout, "Pushing tag %s:%s\r\n", remote, tag)
fmt.Fprintf(stdout, "Pushing image %s:%s\r\n", remote, tag)
// Push the image
if err = graph.PushImage(stdout, img, authConfig); err != nil {
if err = graph.PushImage(stdout, img, registry, token); err != nil {
return err
}
fmt.Fprintf(stdout, "Registering tag %s:%s\r\n", remote, tag)
// And then the tag
if err = graph.pushTag(remote, imgId, tag, authConfig); err != nil {
if err = graph.pushTag(remote, imgId, tag, registry, token); err != nil {
return err
}
return nil
@@ -391,18 +486,155 @@ func (graph *Graph) pushPrimitive(stdout io.Writer, remote, tag, imgId string, a
// Push a repository to the registry.
// Remote has the format '<user>/<repo>
func (graph *Graph) PushRepository(stdout io.Writer, remote string, localRepo Repository, authConfig *auth.AuthConfig) error {
// Check if the remote repository exists/if we have the permission
if !graph.LookupRemoteRepository(remote, authConfig) {
return fmt.Errorf("Permission denied on repository %s\n", remote)
client := graph.getHttpClient()
checksums, err := graph.Checksums(stdout, localRepo)
if err != nil {
return err
}
fmt.Fprintf(stdout, "Pushing repository %s (%d tags)\r\n", remote, len(localRepo))
// For each image within the repo, push them
for tag, imgId := range localRepo {
if err := graph.pushPrimitive(stdout, remote, tag, imgId, authConfig); err != nil {
// FIXME: Continue on error?
return err
imgList := make([]map[string]string, len(checksums))
checksums2 := make([]map[string]string, len(checksums))
uploadedImages, err := graph.getImagesInRepository(remote, authConfig)
if err != nil {
return fmt.Errorf("Error occured while fetching the list: %s", err)
}
// Filter list to only send images/checksums not already uploaded
i := 0
for _, obj := range checksums {
found := false
for _, uploadedImg := range uploadedImages {
if obj["id"] == uploadedImg["id"] && uploadedImg["checksum"] != "" {
found = true
break
}
}
if !found {
imgList[i] = map[string]string{"id": obj["id"]}
checksums2[i] = obj
i += 1
}
}
checksums = checksums2[:i]
imgList = imgList[:i]
imgListJson, err := json.Marshal(imgList)
if err != nil {
return err
}
req, err := http.NewRequest("PUT", INDEX_ENDPOINT+"/repositories/"+remote+"/", bytes.NewReader(imgListJson))
if err != nil {
return err
}
req.SetBasicAuth(authConfig.Username, authConfig.Password)
req.ContentLength = int64(len(imgListJson))
req.Header.Set("X-Docker-Token", "true")
res, err := client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
for res.StatusCode >= 300 && res.StatusCode < 400 {
Debugf("Redirected to %s\n", res.Header.Get("Location"))
req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJson))
if err != nil {
return err
}
req.SetBasicAuth(authConfig.Username, authConfig.Password)
req.ContentLength = int64(len(imgListJson))
req.Header.Set("X-Docker-Token", "true")
res, err = client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
}
if res.StatusCode != 200 && res.StatusCode != 201 {
return fmt.Errorf("Error: Status %d trying to push repository %s", res.StatusCode, remote)
}
var token, endpoints []string
if res.Header.Get("X-Docker-Token") != "" {
token = res.Header["X-Docker-Token"]
Debugf("Auth token: %v", token)
} else {
return fmt.Errorf("Index response didn't contain an access token")
}
if res.Header.Get("X-Docker-Endpoints") != "" {
endpoints = res.Header["X-Docker-Endpoints"]
} else {
return fmt.Errorf("Index response didn't contain any endpoints")
}
for _, registry := range endpoints {
fmt.Fprintf(stdout, "Pushing repository %s to %s (%d tags)\r\n", remote, registry,
len(localRepo))
// For each image within the repo, push them
for tag, imgId := range localRepo {
if err := graph.pushPrimitive(stdout, remote, tag, imgId, registry, token); err != nil {
// FIXME: Continue on error?
return err
}
}
}
checksumsJson, err := json.Marshal(checksums)
if err != nil {
return err
}
req2, err := http.NewRequest("PUT", INDEX_ENDPOINT+"/repositories/"+remote+"/images", bytes.NewReader(checksumsJson))
if err != nil {
return err
}
req2.SetBasicAuth(authConfig.Username, authConfig.Password)
req2.Header["X-Docker-Endpoints"] = endpoints
req2.ContentLength = int64(len(checksumsJson))
res2, err := client.Do(req2)
if err != nil {
return err
}
res2.Body.Close()
if res2.StatusCode != 204 {
return fmt.Errorf("Error: Status %d trying to push checksums %s", res.StatusCode, remote)
}
return nil
}
func (graph *Graph) Checksums(output io.Writer, repo Repository) ([]map[string]string, error) {
var result []map[string]string
checksums := map[string]string{}
for _, id := range repo {
img, err := graph.Get(id)
if err != nil {
return nil, err
}
err = img.WalkHistory(func(image *Image) error {
fmt.Fprintf(output, "Computing checksum for image %s\n", image.Id)
if _, exists := checksums[image.Id]; !exists {
checksums[image.Id], err = image.Checksum()
if err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
}
i := 0
result = make([]map[string]string, len(checksums))
for id, sum := range checksums {
result[i] = map[string]string{
"id": id,
"checksum": sum,
}
i++
}
return result, nil
}
+123 -45
View File
@@ -31,6 +31,8 @@ type Runtime struct {
idIndex *TruncIndex
capabilities *Capabilities
kernelVersion *KernelVersionInfo
autoRestart bool
volumes *Graph
}
var sysInitPath string
@@ -77,12 +79,58 @@ func (runtime *Runtime) containerRoot(id string) string {
return path.Join(runtime.repository, id)
}
func (runtime *Runtime) mergeConfig(userConf, imageConf *Config) {
if userConf.Hostname == "" {
userConf.Hostname = imageConf.Hostname
}
if userConf.User == "" {
userConf.User = imageConf.User
}
if userConf.Memory == 0 {
userConf.Memory = imageConf.Memory
}
if userConf.MemorySwap == 0 {
userConf.MemorySwap = imageConf.MemorySwap
}
if userConf.PortSpecs == nil || len(userConf.PortSpecs) == 0 {
userConf.PortSpecs = imageConf.PortSpecs
}
if !userConf.Tty {
userConf.Tty = userConf.Tty
}
if !userConf.OpenStdin {
userConf.OpenStdin = imageConf.OpenStdin
}
if !userConf.StdinOnce {
userConf.StdinOnce = imageConf.StdinOnce
}
if userConf.Env == nil || len(userConf.Env) == 0 {
userConf.Env = imageConf.Env
}
if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
userConf.Cmd = imageConf.Cmd
}
if userConf.Dns == nil || len(userConf.Dns) == 0 {
userConf.Dns = imageConf.Dns
}
}
func (runtime *Runtime) Create(config *Config) (*Container, error) {
// Lookup image
img, err := runtime.repositories.LookupImage(config.Image)
if err != nil {
return nil, err
}
if img.Config != nil {
runtime.mergeConfig(config, img.Config)
}
if config.Cmd == nil || len(config.Cmd) == 0 {
return nil, fmt.Errorf("No command specified")
}
// Generate id
id := GenerateId()
// Generate default hostname
@@ -103,6 +151,7 @@ func (runtime *Runtime) Create(config *Config) (*Container, error) {
// FIXME: do we need to store this in the container?
SysInitPath: sysInitPath,
}
container.root = runtime.containerRoot(container.Id)
// Step 1: create the container directory.
// This doubles as a barrier to avoid race conditions.
@@ -167,29 +216,6 @@ func (runtime *Runtime) Register(container *Container) error {
// init the wait lock
container.waitLock = make(chan struct{})
// FIXME: if the container is supposed to be running but is not, auto restart it?
// if so, then we need to restart monitor and init a new lock
// If the container is supposed to be running, make sure of it
if container.State.Running {
if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
return err
} else {
if !strings.Contains(string(output), "RUNNING") {
Debugf("Container %s was supposed to be running be is not.", container.Id)
container.State.setStopped(-127)
if err := container.ToDisk(); err != nil {
return err
}
}
}
}
// If the container is not running or just has been flagged not running
// then close the wait lock chan (will be reset upon start)
if !container.State.Running {
close(container.waitLock)
}
// Even if not running, we init the lock (prevents races in start/stop/kill)
container.State.initLock()
@@ -207,6 +233,47 @@ func (runtime *Runtime) Register(container *Container) error {
// done
runtime.containers.PushBack(container)
runtime.idIndex.Add(container.Id)
// When we actually restart, Start() do the monitoring.
// However, when we simply 'reattach', we have to restart a monitor
nomonitor := false
// FIXME: if the container is supposed to be running but is not, auto restart it?
// if so, then we need to restart monitor and init a new lock
// If the container is supposed to be running, make sure of it
if container.State.Running {
if output, err := exec.Command("lxc-info", "-n", container.Id).CombinedOutput(); err != nil {
return err
} else {
if !strings.Contains(string(output), "RUNNING") {
Debugf("Container %s was supposed to be running be is not.", container.Id)
if runtime.autoRestart {
Debugf("Restarting")
container.State.Ghost = false
container.State.setStopped(0)
if err := container.Start(); err != nil {
return err
}
nomonitor = true
} else {
Debugf("Marking as stopped")
container.State.setStopped(-127)
if err := container.ToDisk(); err != nil {
return err
}
}
}
}
}
// If the container is not running or just has been flagged not running
// then close the wait lock chan (will be reset upon start)
if !container.State.Running {
close(container.waitLock)
} else if !nomonitor {
container.allocateNetwork()
go container.monitor()
}
return nil
}
@@ -246,7 +313,7 @@ func (runtime *Runtime) Destroy(container *Container) error {
// Commit creates a new filesystem image from the current state of a container.
// The image can optionally be tagged into a repository
func (runtime *Runtime) Commit(id, repository, tag, comment, author string) (*Image, error) {
func (runtime *Runtime) Commit(id, repository, tag, comment, author string, config *Config) (*Image, error) {
container := runtime.Get(id)
if container == nil {
return nil, fmt.Errorf("No such container: %s", id)
@@ -258,7 +325,7 @@ func (runtime *Runtime) Commit(id, repository, tag, comment, author string) (*Im
return nil, err
}
// Create a new image from the container's base layers + a new layer from container changes
img, err := runtime.graph.Create(rwTar, container, comment, author)
img, err := runtime.graph.Create(rwTar, container, comment, author, config)
if err != nil {
return nil, err
}
@@ -288,9 +355,30 @@ func (runtime *Runtime) restore() error {
return nil
}
func (runtime *Runtime) UpdateCapabilities(quiet bool) {
if cgroupMemoryMountpoint, err := FindCgroupMountpoint("memory"); err != nil {
if !quiet {
log.Printf("WARNING: %s\n", err)
}
} else {
_, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes"))
_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
if !runtime.capabilities.MemoryLimit && !quiet {
log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
}
_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
runtime.capabilities.SwapLimit = err == nil
if !runtime.capabilities.SwapLimit && !quiet {
log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
}
}
}
// FIXME: harmonize with NewGraph()
func NewRuntime() (*Runtime, error) {
runtime, err := NewRuntimeFromDirectory("/var/lib/docker")
func NewRuntime(autoRestart bool) (*Runtime, error) {
runtime, err := NewRuntimeFromDirectory("/var/lib/docker", autoRestart)
if err != nil {
return nil, err
}
@@ -303,27 +391,11 @@ func NewRuntime() (*Runtime, error) {
log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
}
}
if cgroupMemoryMountpoint, err := FindCgroupMountpoint("memory"); err != nil {
log.Printf("WARNING: %s\n", err)
} else {
_, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes"))
_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
if !runtime.capabilities.MemoryLimit {
log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
}
_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
runtime.capabilities.SwapLimit = err == nil
if !runtime.capabilities.SwapLimit {
log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
}
}
runtime.UpdateCapabilities(false)
return runtime, nil
}
func NewRuntimeFromDirectory(root string) (*Runtime, error) {
func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
runtimeRepo := path.Join(root, "containers")
if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
@@ -334,6 +406,10 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) {
if err != nil {
return nil, err
}
volumes, err := NewGraph(path.Join(root, "volumes"))
if err != nil {
return nil, err
}
repositories, err := NewTagStore(path.Join(root, "repositories"), g)
if err != nil {
return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
@@ -360,6 +436,8 @@ func NewRuntimeFromDirectory(root string) (*Runtime, error) {
authConfig: authConfig,
idIndex: NewTruncIndex(),
capabilities: &Capabilities{},
autoRestart: autoRestart,
volumes: volumes,
}
if err := runtime.restore(); err != nil {
+18 -6
View File
@@ -60,8 +60,10 @@ func init() {
panic("docker tests needs to be run as root")
}
NetworkBridgeIface = "testdockbr0"
// Make it our Store root
runtime, err := NewRuntimeFromDirectory(unitTestStoreBase)
runtime, err := NewRuntimeFromDirectory(unitTestStoreBase, false)
if err != nil {
panic(err)
}
@@ -87,11 +89,11 @@ func newTestRuntime() (*Runtime, error) {
return nil, err
}
runtime, err := NewRuntimeFromDirectory(root)
runtime, err := NewRuntimeFromDirectory(root, false)
if err != nil {
return nil, err
}
runtime.UpdateCapabilities(true)
return runtime, nil
}
@@ -273,7 +275,16 @@ func TestAllocatePortLocalhost(t *testing.T) {
t.Fatal(err)
}
defer container.Kill()
time.Sleep(300 * time.Millisecond) // Wait for the container to run
setTimeout(t, "Waiting for the container to be started timed out", 2*time.Second, func() {
for {
if container.State.Running {
break
}
time.Sleep(10 * time.Millisecond)
}
})
conn, err := net.Dial("tcp",
fmt.Sprintf(
"localhost:%s", container.NetworkSettings.PortMapping["5555"],
@@ -293,6 +304,7 @@ func TestAllocatePortLocalhost(t *testing.T) {
string(output),
)
}
container.Wait()
}
func TestRestore(t *testing.T) {
@@ -308,7 +320,7 @@ func TestRestore(t *testing.T) {
t.Fatal(err)
}
runtime1, err := NewRuntimeFromDirectory(root)
runtime1, err := NewRuntimeFromDirectory(root, false)
if err != nil {
t.Fatal(err)
}
@@ -367,7 +379,7 @@ func TestRestore(t *testing.T) {
// Here are are simulating a docker restart - that is, reloading all containers
// from scratch
runtime2, err := NewRuntimeFromDirectory(root)
runtime2, err := NewRuntimeFromDirectory(root, false)
if err != nil {
t.Fatal(err)
}
+22 -6
View File
@@ -2,6 +2,8 @@ package docker
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"github.com/dotcloud/docker/rcli"
@@ -12,7 +14,6 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
"sync"
@@ -396,6 +397,15 @@ func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
return written, err
}
func HashData(src io.Reader) (string, error) {
h := sha256.New()
if _, err := io.Copy(h, src); err != nil {
return "", err
}
return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil
}
type KernelVersionInfo struct {
Kernel int
Major int
@@ -437,17 +447,23 @@ func CompareKernelVersion(a, b *KernelVersionInfo) int {
}
func FindCgroupMountpoint(cgroupType string) (string, error) {
output, err := exec.Command("mount").CombinedOutput()
output, err := ioutil.ReadFile("/proc/mounts")
if err != nil {
return "", err
}
reg := regexp.MustCompile(`^cgroup on (.*) type cgroup \(.*` + cgroupType + `[,\)]`)
// /proc/mounts has 6 fields per line, one mount per line, e.g.
// cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0
for _, line := range strings.Split(string(output), "\n") {
r := reg.FindStringSubmatch(line)
if len(r) == 2 {
return r[1], nil
parts := strings.Split(line, " ")
if len(parts) == 6 && parts[2] == "cgroup" {
for _, opt := range strings.Split(parts[3], ",") {
if opt == cgroupType {
return parts[1], nil
}
}
}
}
return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
}