Compare commits

...

419 Commits

Author SHA1 Message Date
Solomon Hykes
ecd1fff9b0 Fix formatting in Changelog 2013-06-21 20:22:14 -06:00
Daniel Mizyrycki
b0f12bd5e8 Merge pull request #988 from dotcloud/526-packaging-ubuntu
Packaging|Ubuntu, issue #526: Follow init permission pattern for docker.conf
2013-06-21 18:03:12 -07:00
Daniel Mizyrycki
5d61ec11e3 Packaging|Ubuntu, issue #526: Follow init permission pattern for docker.conf 2013-06-21 17:57:45 -07:00
Guillaume J. Charmes
c792c0a6c9 Merge pull request #986 from dotcloud/bump_0.4.5
Bump version to 0.4.5
2013-06-21 17:13:38 -07:00
Solomon Hykes
d9d2540162 Small copy improvements in Changelog 2013-06-21 17:09:49 -07:00
Guillaume J. Charmes
f5d08fc49c Bump version to 0.4.5 2013-06-21 17:01:01 -07:00
Victor Vieux
b24759af1c Merge pull request #959 from dotcloud/947-add_ps_size_option
+ Client: Add docker ps -s option to display containers' sizes
2013-06-21 16:10:45 -07:00
Victor Vieux
4d1692726b merge master and add doc 2013-06-22 01:08:20 +02:00
Victor Vieux
1581ed52ba consistent codebase fix 2013-06-21 22:55:33 +00:00
Guillaume J. Charmes
de1a5a75cc Merge pull request #848 from dotcloud/builder_server-3
Improve Docker build
2013-06-21 14:55:08 -07:00
Guillaume J. Charmes
169ef21de7 Remove deprecated INSERT from documentation 2013-06-21 14:12:52 -07:00
Guillaume J. Charmes
d0fa6927f8 Update api docs 2013-06-21 13:51:48 -07:00
Victor Vieux
63e8a4ac74 Merge pull request #970 from titanous/go1.1-unreachable
Remove code unreachable using Go 1.1
2013-06-21 10:44:40 -07:00
Victor Vieux
459230d3f9 Merge pull request #980 from ToothlessGear/fix-progressbar
* Client: fix progressbar output
2013-06-21 10:42:14 -07:00
Guillaume J. Charmes
070e1aec7e Merge pull request #975 from unclejack/891-mark_command_as_optional
891 mark command as optional for docker run
2013-06-21 10:22:52 -07:00
Marcus Farkas
3ac68f1966 Client: better progressbar output 2013-06-21 17:19:27 +02:00
Victor Vieux
5ccde4dffc Merge pull request #801 from dotcloud/build_docker_static
* Makefile: Add link flags in order to link statically and without debug symbols
2013-06-21 02:30:57 -07:00
Victor Vieux
dc847001a5 Merge pull request #888 from dotcloud/fix-auth
* Auth: fix auth small issue in case you change your password on index.io
2013-06-21 02:22:01 -07:00
Victor Vieux
639833aaf5 fix tests 2013-06-21 09:20:57 +00:00
Victor Vieux
8f2a80804c Merge branch 'master' into fix-auth 2013-06-21 09:18:03 +00:00
Victor Vieux
78842970cf Merge pull request #976 from dotcloud/fix_doc_post
fix typo in documentation
2013-06-21 02:14:39 -07:00
Victor Vieux
8e7d4cda07 fix doc post 2013-06-21 11:13:13 +02:00
Victor Vieux
5b3ad0023b inverse if 2013-06-21 09:06:09 +00:00
unclejack
6a1279fb90 docs: mark command as optional for docker run 2013-06-21 11:07:14 +03:00
unclejack
66910a7602 mark command as optional for docker run 2013-06-21 11:06:41 +03:00
Solomon Hykes
d9bce2defd - Builder: return an error when the build fails 2013-06-20 22:15:19 -07:00
Solomon Hykes
352991bdf4 Merge branch 'simpler-build-upload' (#900) into builder_server-3 (#848) 2013-06-20 22:02:36 -07:00
Solomon Hykes
4383d7b603 Merge pull request #953 from rhysh/952-swapaccount-docs
- Documentation: fix inconsistent/outdated instructions for setting up "swapaccount"
2013-06-20 20:49:44 -07:00
Solomon Hykes
89ae56820a Merge pull request #965 from freegenie/patch-1
* Documentation: use sudo for quick install script
2013-06-20 20:48:42 -07:00
Solomon Hykes
17489cac1a Merge pull request #972 from titanous/update-authors
Update AUTHORS
2013-06-20 20:42:52 -07:00
Solomon Hykes
c1a5318d8e Merge branch 'build-add-file' into simpler-build-upload
Conflicts:
	buildfile_test.go
2013-06-20 20:42:19 -07:00
Jonathan Rudenberg
b0b690cf23 Update AUTHORS 2013-06-20 23:29:20 -04:00
Solomon Hykes
86e83186b5 Merge branch 'master' into simpler-build-upload
Conflicts:
	commands.go
2013-06-20 20:25:59 -07:00
Solomon Hykes
36d610a388 - Builder: fixed a regression in ADD. Improved regression tests for build behavior. 2013-06-20 20:20:16 -07:00
Jonathan Rudenberg
50b70eeb68 Remove code unreachable using Go 1.1 2013-06-20 23:19:44 -04:00
Solomon Hykes
cc0f59742f * Builder: simplified unit tests. The tests are now embedded in the build itself. Yeah baby. 2013-06-20 20:16:39 -07:00
Solomon Hykes
09dd7f14de Merge pull request #969 from dotcloud/fix_logs
- Remote API: Fix a bug which caused 'docker logs' to fail
2013-06-20 18:33:33 -07:00
Guillaume J. Charmes
b419699ab8 Use hijack for logs instead of stream 2013-06-20 18:18:36 -07:00
Guillaume J. Charmes
08825fa611 remove unused files 2013-06-20 16:31:11 -07:00
Solomon Hykes
02f0c1e46d Bump version to 0.4.4 2013-06-20 14:33:59 -07:00
Solomon Hykes
dbfb3eb923 - Builder: hotfix for bug introduced in 3adf9ce04e 2013-06-20 14:29:34 -07:00
Solomon Hykes
e43323221b Merge branch 'master' into simpler-build-upload
Conflicts:
	api.go
	builder_client.go
	commands.go
2013-06-20 14:19:09 -07:00
Fabrizio Regini
da06349723 Update README.md 2013-06-20 23:15:38 +02:00
Solomon Hykes
cff2187a4c Fixed API version numbers in api docs 2013-06-20 12:30:02 -07:00
Guillaume J. Charmes
a078d3c872 Merge pull request #950 from dotcloud/bump_0.4.3
Bumped version to 0.4.3
2013-06-20 12:24:55 -07:00
Guillaume J. Charmes
da5bb4db96 Bumped version to 0.4.3 2013-06-20 12:23:14 -07:00
Daniel Mizyrycki
1b19939742 Merge pull request #946 from unclejack/speed_up_dockerbuilder_image_creation
dockerbuilder : batch apt-get install operations for speed
2013-06-20 11:51:43 -07:00
Guillaume J. Charmes
930e1d8830 Merge pull request #941 from dotcloud/makefile_test_subpackages
gofmt and test sub directories in makefile
2013-06-20 11:18:37 -07:00
Guillaume J. Charmes
fa68fe6ff3 Merge pull request #938 from dotcloud/add_unix_socket-feature
* Runtime: Add unix socket and multiple -H
2013-06-20 11:17:16 -07:00
Guillaume J. Charmes
21a5a6202d Merge pull request #907 from dotcloud/go1.1_cookie_jar-feature
* Runtime: use go 1.1 cookiejar and remove ResetClient
2013-06-20 10:48:36 -07:00
Solomon Hykes
db60337598 Makefile: added missing -a option 2013-06-20 10:39:09 -07:00
Guillaume J. Charmes
c5be64fec4 Add link flags in order to link statically and without debug symbols 2013-06-20 10:33:54 -07:00
Guillaume J. Charmes
659e846006 Merge branch 'master' into builder_server-3
Conflicts:
	docs/sources/use/builder.rst
2013-06-20 10:27:12 -07:00
Solomon Hykes
d8f56352da Merge pull request #961 from dotcloud/933-warning_rm-running
* Runtime: refuse to remove a running container
2013-06-20 10:25:02 -07:00
Guillaume J. Charmes
d1a3d020aa Merge pull request #913 from dotcloud/fix_detach_eof
- Runtime: Impossible to detach from attached container fix
2013-06-20 10:21:19 -07:00
Guillaume J. Charmes
8807b7dd46 Merge pull request #909 from dotcloud/fix_auth_tests
Fix the auth tests
2013-06-20 10:14:55 -07:00
Daniel Mizyrycki
cd155a1f25 Merge pull request #962 from dotcloud/960-packaging-ubuntu
Packaging|ubuntu, issue #960: Add docker PPA staging in release process
2013-06-20 09:03:15 -07:00
Daniel Mizyrycki
d8887f3488 Packaging|ubuntu, issue #960: Add docker PPA staging in release process 2013-06-20 08:57:28 -07:00
Victor Vieux
1c841d4fee add warning when you rm a running container 2013-06-20 15:45:30 +00:00
Victor Vieux
da199846d2 use strconv.ParseBool in getBoolParam 2013-06-20 14:34:58 +00:00
Victor Vieux
bd04d7d475 add ps -s 2013-06-20 14:19:50 +00:00
Victor Vieux
5f93aa0ecf rebase master 2013-06-20 13:56:36 +00:00
Victor Vieux
05796bed57 update docs 2013-06-20 12:34:08 +00:00
Solomon Hykes
8a131dffb6 Merge pull request #948 from dotcloud/registry_pathencode
* Registry: Use opaque requests when we need to preserve urlencoding in registry requests
2013-06-19 22:41:16 -07:00
Solomon Hykes
79efcb545d Merge branch 'master' into simpler-build-upload 2013-06-19 18:48:19 -07:00
Daniel Mizyrycki
88dcba3482 Packaging|ubuntu, issue #954: Generate debian/changelog from main CHANGELOG.md 2013-06-19 16:32:51 -07:00
Guillaume J. Charmes
754609ab69 Merge pull request #945 from globalcitizen/master
Security warnings in LXC configuration
2013-06-19 15:06:48 -07:00
Solomon Hykes
d6ab71f450 * Remote API: updated docs for 1.3 2013-06-19 15:03:33 -07:00
Solomon Hykes
55edbcd02f * Builder: remove duplicate unit test 2013-06-19 14:59:42 -07:00
Solomon Hykes
90dde9beab *Builder: warn pre-1.3 clients that they need to upgrade. This breaks semver, but our API should still be in 0.X versioning, in which case semver allows breaking changes. 2013-06-19 14:59:28 -07:00
Rhys Hiltner
5fc1329b2f the kernel needs "swapaccount=1" set - some docs are updated, this one seems to have slipped through
Fixes #952
2013-06-19 14:45:03 -07:00
Solomon Hykes
9c8085a0aa Merge pull request #951 from dotcloud/add-fix
* Builder: correct the behavior of ADD when copying directories.
2013-06-19 14:36:09 -07:00
Solomon Hykes
507ea757a5 * Builder: correct the behavior of ADD when copying directories. 2013-06-19 14:26:11 -07:00
Guillaume J. Charmes
7e065aaacd Merge pull request #917 from dotcloud/pull_pool
- Runtime: Forbid parralel push/pull for a single image/repo. Fixes #311
2013-06-19 14:11:29 -07:00
shin-
0312bbc535 Use opaque requests when we need to preserve urlencoding in registry requests 2013-06-19 13:49:45 -07:00
Solomon Hykes
a056f1deec Merge pull request #924 from eliasp/remove-ifconfig-usage
* Documentation: replace `ifconfig` in docs with `iproute`
2013-06-19 13:20:35 -07:00
Solomon Hykes
fdaefe6997 Merge pull request #944 from hansent/patch-1
* Documentation: use https repo url to clone for dev setup instructions
2013-06-19 13:17:08 -07:00
unclejack
88279439af batch apt-get install operations for speed
The dockerbuilder Dockerfile was installing one package per apt-get
install operation.

This changes it so that consecutive run apt-get install operations are
batched into a single operation.
2013-06-19 22:07:56 +03:00
Victor Vieux
2d6a49215c add testall rule 2013-06-19 18:21:53 +00:00
Guillaume J. Charmes
a7e14a3065 hotfix: nil pointer uppon some registry error 2013-06-19 11:08:19 -07:00
Guillaume J. Charmes
a660cc0d01 Merge pull request #934 from dotcloud/fix-add-behavior
* Build: Stabilize ADD behavior
2013-06-19 10:56:39 -07:00
globalcitizen
788d66f409 Add note about lxc.cap.keep > lxc.cap.drop 2013-06-20 00:39:35 +07:00
globalcitizen
96988a37f5 Add healthy procfs/sysfs warnings 2013-06-20 00:37:08 +07:00
Solomon Hykes
b368d21568 Merge branch 'master' into fix-add-behavior 2013-06-19 10:31:50 -07:00
Thomas Hansen
c88b763e80 use https repo url to clone for dev setup instructions
the git clone line in the dev setup instructions does not work as is, unless the user has write access
2013-06-19 11:38:58 -05:00
Victor Vieux
ec3c89e57c Merge pull request #849 from dotcloud/improve_progressbar_pull
* Client: HumanReadable ProgressBar sizes in pull
2013-06-19 08:02:40 -07:00
Victor Vieux
5dcab2d361 gofmt and test sub directories in makefile 2013-06-19 14:50:58 +00:00
Victor Vieux
5f7e98be20 Merge pull request #930 from andrewmunsell/patch-1
Fix Mac OS X installation instructions URL
2013-06-19 07:31:24 -07:00
Victor Vieux
d52af3f58f Merge branch 'master' into add_unix_socket-feature 2013-06-19 12:49:27 +00:00
Victor Vieux
063c838c92 update docs 2013-06-19 12:48:50 +00:00
Victor Vieux
9632bf2287 add tests 2013-06-19 12:40:01 +00:00
Victor Vieux
dede1585ee add the possibility to use multiple -H 2013-06-19 12:31:54 +00:00
Solomon Hykes
5be7b9af3e * Builder: fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented. 2013-06-18 20:28:49 -07:00
Daniel Mizyrycki
0ae778c881 Merge pull request #788 from samjsharpe/master
Vagrantfile: Add support for VMWare Fusion provider
2013-06-18 18:35:25 -07:00
Andrew Munsell
1f8b679b18 Fix Mac OS X installation instructions URL 2013-06-18 19:19:07 -06:00
Guillaume J. Charmes
ee5df76579 Merge pull request #885 from dotcloud/remove_bsdtar
* Runtime: Remove bsdtar dependency
2013-06-18 17:24:26 -07:00
Guillaume J. Charmes
b431720dac Merge branch 'remove_bsdtar' of https://github.com/dotcloud/docker into remove_bsdtar 2013-06-18 17:23:22 -07:00
Guillaume J. Charmes
42ce68894a Fix issue within TestDelete. The archive is now consumed by graph functions 2013-06-18 17:22:32 -07:00
Guillaume J. Charmes
c063fc0238 Merge branch 'master' into fix_detach_eof
Conflicts:
	commands.go
2013-06-18 17:15:31 -07:00
Guillaume J. Charmes
0a9ac63a05 Merge pull request #916 from dotcloud/race_attach-fix
- Runtime: Fix race condition within Run command when attaching.
2013-06-18 17:13:38 -07:00
Guillaume J. Charmes
6dccdd657f remove offline mode from auth unit tests 2013-06-18 17:09:47 -07:00
Guillaume J. Charmes
34a434616a Merge branch 'master' into builder_server-3
Conflicts:
	buildfile.go
2013-06-18 16:12:30 -07:00
Elias Probst
bc9b91e501 Use the canonical 'ip' commands to make it easier for new 'iproute2' users to understand the usage. 2013-06-19 00:57:43 +02:00
Solomon Hykes
edbd3da33a Merge pull request #927 from dotcloud/nicer-build-output
* Builder: nicer output for 'docker build'
2013-06-18 15:48:00 -07:00
Guillaume J. Charmes
32e8f9beca Merge pull request #918 from dotcloud/hisotry_lookup
Add image lookup to history command
2013-06-18 15:36:05 -07:00
Guillaume J. Charmes
84ceeaa870 Update documentaiton 2013-06-18 14:36:35 -07:00
Solomon Hykes
cdeaba2acf Updated FIXME 2013-06-18 13:02:12 -07:00
Solomon Hykes
c0b82bd807 Fix incorrect docs for 'docker build' 2013-06-18 12:52:37 -07:00
Solomon Hykes
88e35b6f80 Merge pull request #926 from josephholsten/fix-znc-example
- Documentation: fix missing command in irc bouncer example
2013-06-18 12:37:21 -07:00
Guillaume J. Charmes
6e17cc45ea Fix merge issue 2013-06-18 12:33:06 -07:00
Solomon Hykes
cb9d0fd3bc Nicer output for 'docker build' 2013-06-18 12:26:56 -07:00
Victor Vieux
3adf9ce04e add basic support for unix sockets 2013-06-18 18:59:56 +00:00
Elias Probst
c2e95997d4 Fixed #923 by replacing the usage of 'ifconfig' with 'ip a' where appropriate and added a note to use 'ip a' instead of 'ifconfig' for a screencast transscript. 2013-06-18 19:55:59 +02:00
Guillaume J. Charmes
808faa6371 * API: Send all tags on History API call 2013-06-18 10:31:07 -07:00
Solomon Hykes
6f511ac29b Remove bsdtar dependency in various install scripts 2013-06-18 10:23:45 -07:00
Guillaume J. Charmes
3dc93e390a Remove useless goroutine 2013-06-18 10:10:03 -07:00
Guillaume J. Charmes
e2d034e488 Remove useless goroutine 2013-06-18 10:06:26 -07:00
Guillaume J. Charmes
86205540d8 Merge branch 'master' into race_attach-fix 2013-06-18 10:03:34 -07:00
Andy Rothfusz
702c3538a4 Merge pull request #921 from dhrp/docs-redirects-and-fixes
Fixes on documentation. LGTM
2013-06-18 09:58:24 -07:00
Victor Vieux
069a7c1e99 Merge pull request #914 from ToothlessGear/fix-version-output
* Client: Fix docker version's git commit output
2013-06-18 05:56:42 -07:00
Daniel Mizyrycki
2e7649beda Merge pull request #920 from dotcloud/919-packaging
Packaging, issue 919: Bump version to 0.4.2
2013-06-18 01:08:03 -07:00
Sam J Sharpe
8281a0fa1c Vagrantfile: Add support for VMWare Fusion provider
As a user who has blown $150 on VMWare Fusion and vagrant-vmware, I
would like to use my new shiny to hack on Docker. Docker already has a
multi-provider Vagrantfile, so adding another one presents little risk.

Known Issues:

- The docker install of a new kernel breaks the Vagrant shared folder.
    - This seems to be because the VMWare hgfs module doesn't build
      against a 3.8 kernel.
    - I don't believe that shared folder support is actually in use
2013-06-18 06:19:14 +01:00
Thatcher Peskens
3491d7d2f1 Fixed on documentation.
* replaced previously removed concepts/containers and concepts/introcution by a redirect
* moved orphan index/varable to workingwiththerepository
* added favicon to the layout.html
* added redirect_home which is a http refresh redirect. It works like a 301 for google
* fixed an issue in the layout that would make it break when on small screens
2013-06-17 20:16:56 -07:00
Daniel Mizyrycki
e664a46ff3 Packaging, issue 919: Bump version to 0.4.2 2013-06-17 19:50:31 -07:00
Solomon Hykes
0809f649d3 * Builder: upload progress bar
Fix progress bar
2013-06-17 18:49:16 -07:00
Guillaume J. Charmes
02a002d264 Update documentation 2013-06-17 18:41:13 -07:00
Guillaume J. Charmes
3bfc822578 * API: Add tag lookup to history command. Fixes #882 2013-06-17 18:39:30 -07:00
Guillaume J. Charmes
02c291d13b Fix bug on compression detection when chunck < 10bytes 2013-06-17 18:11:58 -07:00
Marcus Farkas
b25bcf1a66 fix docker version git output 2013-06-17 23:32:48 +00:00
Guillaume J. Charmes
fe204e6f48 - Runtime: Forbid parralel push/pull for a single image/repo. Fixes #311 2013-06-17 16:10:00 -07:00
Guillaume J. Charmes
2b6ca38728 Remove Run race condition 2013-06-17 15:45:08 -07:00
Guillaume J. Charmes
c106ed32ea Move the attach prevention from server to client 2013-06-17 15:40:04 -07:00
Joseph Anthony Pasquale Holsten
2626d88a21 fix missing command in irc bouncer example 2013-06-17 14:50:58 -07:00
Guillaume J. Charmes
3a0ffbc772 - Runtime: Fixes #884 enforce stdout/err sync by merging the stream 2013-06-17 14:44:35 -07:00
Daniel Mizyrycki
bd9bf9b646 Packaging|dockerbuilder, issue #761: use docker-golang PPA. Add Dockerfile header 2013-06-17 14:08:50 -07:00
Victor Vieux
7b6f50772c Merge pull request #912 from dotcloud/bump_0.4.1
Bumped version to 0.4.1
2013-06-17 14:06:17 -07:00
Guillaume J. Charmes
555552340d Merge branch 'master' into builder_server-3
Conflicts:
	buildfile_test.go
2013-06-17 14:01:32 -07:00
Solomon Hykes
6e2c32eb9a Merge pull request #911 from dotcloud/add_port_redirection_doc
* Documentation: add port redirection doc
2013-06-17 13:57:08 -07:00
Solomon Hykes
22b0a38df5 Merge pull request #897 from dotcloud/fix-overlapping-add
* Builder: ADD improvements: use tar for copy + automatically unpack local archives
2013-06-17 13:32:34 -07:00
Solomon Hykes
cb58e63fc5 Typo 2013-06-17 14:28:04 -06:00
Solomon Hykes
8626598753 Added content to port redirect doc 2013-06-17 13:25:50 -07:00
Victor Vieux
36231345f1 add port redirection doc 2013-06-17 22:05:58 +02:00
Victor Vieux
e8f001d451 Bumped version to 0.4.1 2013-06-17 19:15:21 +00:00
Guillaume J. Charmes
13e03a6911 Fix the auth tests and add the offline mode 2013-06-17 11:29:02 -07:00
Victor Vieux
fde82f448f use go 1.1 cookiejar and revome ResetClient 2013-06-17 18:13:40 +00:00
Guillaume J. Charmes
79b3265ef1 Merge branch 'master' into builder_server-3
Conflicts:
	buildfile.go
2013-06-17 11:09:53 -07:00
Daniel Mizyrycki
389db5f598 Merge pull request #887 from dotcloud/761-packaging-dockerbuilder
Packaging, dockerbuilder: Automate pushing docker binary packages
2013-06-17 08:40:01 -07:00
Solomon Hykes
fe88b5068d Merge branch 'master' into simpler-build-upload 2013-06-15 12:35:26 -07:00
Solomon Hykes
6746c385bd FIXMEs 2013-06-15 12:24:20 -07:00
Solomon Hykes
f50e40008f * Builder: added a regression test for #895 2013-06-15 11:35:56 -07:00
Solomon Hykes
061f8d12e0 * Builder: reorganized unit tests for better code reuse, and to test non-empty contexts 2013-06-15 11:07:49 -07:00
Solomon Hykes
38554fc2a7 * Builder: simplify the upload of the build context. Simply stream a tarball instead of multipart upload with 4 intermediary buffers. Simpler, less memory usage, less disk usage, and faster. 2013-06-15 09:38:18 -07:00
Solomon Hykes
cc7de8df75 Removed deprecated file builder_client.go 2013-06-15 09:30:52 -07:00
Solomon Hykes
30f604517a Merge pull request #895 from dotcloud/build-fixes
- Builder: fix a bug which caused builds to fail if ADD was the first command
2013-06-15 09:23:41 -07:00
Solomon Hykes
080f35fe65 Fix a bug which caused builds to fail if ADD was the first command 2013-06-15 09:16:35 -07:00
Guillaume J. Charmes
78f86ea502 Merge branch 'master' into builder_server-3
Conflicts:
	utils/utils.go
2013-06-14 17:08:39 -07:00
Solomon Hykes
5b8287617d + Builder: ADD of a local file will detect tar archives and unpack them
into the container instead of copying them as a regular file.

* Builder: ADD uses tar/untar for copies instead of calling 'cp -ar'.
	This is more consistent, reduces the number of dependencies, and
	fixe #896.
2013-06-14 16:43:39 -07:00
Solomon Hykes
5799806414 FIXMEs 2013-06-14 16:29:19 -07:00
Guillaume J. Charmes
76a568fc97 Fix merge issue 2013-06-14 16:08:08 -07:00
Solomon Hykes
14265d9a18 Various FIXME items 2013-06-14 15:11:34 -07:00
Solomon Hykes
17235eb089 Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-06-14 15:07:05 -07:00
Solomon Hykes
7f118519eb Remove duplicate 'WARNING' 2013-06-14 14:46:08 -07:00
Solomon Hykes
250e47e2eb Merge branch 'dns_server_side'
+ Configure dns configuration host-wide with 'docker -d -dns'
+ Detect faulty DNS configuration and replace it with a public default
2013-06-14 14:39:05 -07:00
Guillaume J. Charmes
f413fb8e56 Merge pull request #857 from edx/856-vagrant-port-forwarding
* Vagrantfile: Add an option to forward all ports to the vagrant host that have been ex...
2013-06-14 14:37:55 -07:00
Guillaume J. Charmes
f0e43dcdb1 Merge pull request #607 from dotcloud/expose_api_port_vagrant-feature
* Vagrantfile: Add the rest api port to vagrantfile's port_forward
2013-06-14 14:35:53 -07:00
Guillaume J. Charmes
abf85b2508 Merge branch 'master' into remove_bsdtar
Conflicts:
	docs/sources/contributing/devenvironment.rst
2013-06-14 14:34:30 -07:00
Guillaume J. Charmes
813771e6b7 Merge pull request #892 from unclejack/validate_memory_limits
* Runtime: validate memory limits & error out if it's less than 524288
2013-06-14 14:32:28 -07:00
unclejack
d3f83a6592 add test: fail to create container if mem limit < 512KB 2013-06-14 22:55:00 +03:00
Andy Rothfusz
7958f1f694 Add examples for local import. 2013-06-14 13:42:59 -06:00
Guillaume J. Charmes
4a02c6dab1 Merge pull request #816 from unclejack/524-fix_aufs_links_related_warnings
524 fix aufs links related warnings
2013-06-14 12:32:47 -07:00
Guillaume J. Charmes
165d343d06 Merge pull request #663 from dotcloud/662-fix_push_html_404-fix
* Registry: add regexp check on repo's name
2013-06-14 12:30:44 -07:00
Guillaume J. Charmes
60fd7d686d Merge branch 'master' into improve_progressbar_pull 2013-06-14 12:01:40 -07:00
Solomon Hykes
c701de939f Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-06-14 11:58:46 -07:00
Guillaume J. Charmes
05b87d2d5b Merge pull request #868 from dotcloud/postupload-endpoints-header
- Registry: Send X-Docker-Endpoints at the end of a push
2013-06-14 11:53:54 -07:00
Guillaume J. Charmes
78e4a385f7 Merge branch 'master' into postupload-endpoints-header
Conflicts:
	server.go
2013-06-14 11:50:58 -07:00
unclejack
822abab17e install aufs-tools when setting up the testing vagrant box 2013-06-14 21:41:12 +03:00
unclejack
f1d16ea003 install aufs-tools when setting up the hack vagrant box 2013-06-14 21:41:12 +03:00
unclejack
fb7eaf67d1 add aufs-tools package to dev env docs page 2013-06-14 21:41:12 +03:00
unclejack
e53721ef69 add aufs-tools to lxc-docker dependencies 2013-06-14 21:38:15 +03:00
unclejack
2f67a62b5b run auplink before unmounting aufs 2013-06-14 21:38:15 +03:00
Guillaume J. Charmes
79fe864d9a Update docs 2013-06-14 10:58:16 -07:00
Guillaume J. Charmes
6f7de49aa8 Add unit tests for tar/untar with multiple compression + detection 2013-06-14 10:47:49 -07:00
unclejack
9ee11161bf validate memory limits & error out if less than 512 KB 2013-06-14 19:52:44 +03:00
Victor Vieux
90f6bdd6e4 update docs, remove config file on 401 2013-06-14 13:38:51 +00:00
Victor Vieux
e49f82b9e1 update docs 2013-06-14 10:10:26 +00:00
Victor Vieux
ddf5a1940f Merge branch 'master' into 22-add_sizes_images_and_containers-feature 2013-06-14 10:05:06 +00:00
Victor Vieux
00cf2a1fa2 fix virtual size on images 2013-06-14 10:05:01 +00:00
Victor Vieux
9cc72ff1a9 fix auth in case you change your password on index.io 2013-06-14 09:53:48 +00:00
Daniel Mizyrycki
3384943cd3 Packaging, dockerbuilder: Automate pushing docker binary packages 2013-06-13 22:14:43 -07:00
Guillaume J. Charmes
2f14dae83f Add build UT 2013-06-13 18:52:41 -07:00
Guillaume J. Charmes
f03ebc20aa Fix issue with ADD 2013-06-13 18:42:27 -07:00
Guillaume J. Charmes
4b4918f2a7 Merge branch 'master' into builder_server-3
Conflicts:
	buildfile.go
	commands.go
	docs/sources/api/docker_remote_api.rst
2013-06-13 18:11:22 -07:00
Guillaume J. Charmes
0425f65e63 Remove bsdtar by checking magic 2013-06-13 17:53:38 -07:00
Guillaume J. Charmes
452128f0da Remove run() where it is not needed within the builder 2013-06-13 15:18:15 -07:00
Guillaume J. Charmes
f5fe3ce34e Remove run from non-running commmands 2013-06-13 15:08:53 -07:00
Guillaume J. Charmes
d0084ce5f2 Remove run from the ADD instruction 2013-06-13 14:57:50 -07:00
Guillaume J. Charmes
2eaa0a1dd7 Fix non-tty run issue 2013-06-13 12:57:35 -07:00
Guillaume J. Charmes
8085754507 Merge pull request #751 from dotcloud/660-auth_client-feature
* Registry: Move auth to the client
2013-06-13 11:52:40 -07:00
Victor Vieux
c46382ba29 rebase master 2013-06-13 17:58:06 +00:00
Guillaume J. Charmes
b38c6929be Updated build usage 2013-06-13 10:50:55 -07:00
Guillaume J. Charmes
42d1c36a5c Fix merge issue 2013-06-13 10:25:43 -07:00
Guillaume J. Charmes
51a4b65101 Merge pull request #883 from unclejack/build-docker-with-go1.1.1
use Go 1.1.1 to build docker
2013-06-13 10:20:38 -07:00
Guillaume J. Charmes
30fb45c494 Merge pull request #799 from dotcloud/691-run_id-feature
* Runtime: allow docker run <name>:<id>
2013-06-13 10:14:40 -07:00
Victor Vieux
9cdd39e0d7 Merge branch 'master' into 691-run_id-feature 2013-06-13 13:18:43 +00:00
Victor Vieux
45a8945746 added test 2013-06-13 13:17:56 +00:00
Victor Vieux
697282d6ad Merge pull request #804 from dotcloud/no_stdout_stale-fix
*Runtime: Fix stale command when stdout is not allocated
2013-06-13 04:22:29 -07:00
unclejack
78a76ad50e use Go 1.1.1 to build docker 2013-06-13 08:59:41 +03:00
Solomon Hykes
5ecfe13be9 Merge branch '610-improve_rmi-feature'
* Runtime: improved image removal to garbage-collect unreferenced parents
- Runtime: fixed image removal to cleanly remove tags and repositories
2013-06-12 20:30:07 -07:00
Guillaume J. Charmes
0bc1c6d57a Merge pull request #826 from dotcloud/825-move_xino_shm-fix
- Runtime: fix aufs mount on ubuntu13.04+btrfs
2013-06-12 17:20:42 -07:00
Solomon Hykes
f57175cbad FIXME: a loose collection of FIXMEs for internal use by the maintainers 2013-06-12 15:28:59 -07:00
Sam Alba
81a11a3c30 Update NOTICE 2013-06-12 15:50:30 -06:00
Sam Alba
04cca097ae Update README.md 2013-06-12 15:50:09 -06:00
Andy Rothfusz
48897b5fa1 Merge pull request #845 from unclejack/841-update_docs_no_add_without_context
841 - docs: warn about the transmission of data to the docker daemon & ADD without context
2013-06-12 14:25:44 -07:00
Solomon Hykes
ecae342434 New roadmap item: advanced port redirections 2013-06-12 10:50:47 -07:00
Victor Vieux
f2383151cb bump to master 2013-06-12 17:39:32 +00:00
Solomon Hykes
b4565af256 Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-06-12 10:23:14 -07:00
Guillaume J. Charmes
c85e775162 Merge pull request #844 from dotcloud/843-inspect_multiple_params-feature
* Runtime: allow multiple params in inspect
2013-06-12 10:18:42 -07:00
Guillaume J. Charmes
3491df6edb Merge pull request #852 from dotcloud/556-docker-search-fmt
Remove CR/NL from description in docker CLI
2013-06-12 10:17:05 -07:00
Guillaume J. Charmes
0e6ec57996 Merge pull request #874 from fsouza/fix-build-newline
- Builder: don't ignore last line in Dockerfile when it doesn't end with \n
2013-06-12 10:15:00 -07:00
Guillaume J. Charmes
f37b158982 Merge pull request #877 from fsouza/fix-hijack
- Runtime: use in instead of os.Stdin in hijack
2013-06-12 10:14:37 -07:00
Francisco Souza
da54abaf2e commands: use in instead of os.Stdin in hijack 2013-06-12 09:54:37 -03:00
Solomon Hykes
092c761cec Merge pull request #853 from kencochrane/registry-api-1.1-fix
* Documentation: separate the registry and index API's into their own docs
2013-06-11 18:34:10 -07:00
Solomon Hykes
5edafd6284 Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-06-11 11:21:57 -07:00
Solomon Hykes
d64f105b44 Added a readme explaining the role of each API 2013-06-11 10:39:02 -07:00
Victor Vieux
2d5eda5141 Merge pull request #864 from dotcloud/851-choose_public_port-feature
* Runtime: you can now specify public port (ex: -p 80:4500)
2013-06-11 10:11:10 -07:00
Solomon Hykes
be15d5f2d9 Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-06-11 10:09:34 -07:00
Solomon Hykes
5918a5a322 More principles. Raw and unstructured to spawn discussion. 2013-06-11 09:27:36 -07:00
Solomon Hykes
f8af296e6f Merge pull request #865 from dotcloud/errors_commands-fix
Display StatusText as error when empty body in commands.go
2013-06-11 09:16:54 -07:00
Solomon Hykes
432e18990b Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-06-11 08:58:23 -07:00
Francisco Souza
2e9403b047 build: don't ignore last line in Dockerfile when it doesn't end with \n 2013-06-11 11:39:06 -03:00
Victor Vieux
3ea6a2c7c3 add Michael Crosby to AUTHORS 2013-06-11 10:17:39 +00:00
Victor Vieux
20bf0e00e8 * Remote Api: Add flag to enable cross domain requests 2013-06-11 10:12:36 +00:00
Michael Crosby
dd53c457d7 Add OPTIONS to route map
Move the OPTIONS method registration into the existing
route map.  Also add support for empty paths in
the map.
2013-06-10 16:10:40 -09:00
Michael Crosby
ac599d6528 Add explicit status response to OPTIONS handler
Write the http.StatusOK header in the OPTIONS
handler and update the unit tests to refer to the
response code using the const from the http package.
2013-06-10 14:44:10 -09:00
Andy Rothfusz
ca4597e9d7 Add links to libraries, fix #800 2013-06-10 15:22:34 -07:00
Andy Rothfusz
eeea9ac946 Add list of Docker Remote API Client Libraries. Fixes #800. 2013-06-10 15:17:27 -07:00
Michael Crosby
0a28628c02 Add Cors and OPTIONS route unit tests
Move creating the router and populating the
routes to a separate function outside of
ListenAndServe to allow unit tests to make
assertions on the configured routes and handler
funcs.
2013-06-10 13:02:40 -09:00
Victor Vieux
bcc4754dc1 Merge pull request #869 from fsouza/fix-api-docs
docs/api/remote: fix rst syntax in the "Search images" section
2013-06-10 14:21:39 -07:00
Victor Vieux
66d9a73362 rebump 2013-06-10 21:05:54 +00:00
Andy Rothfusz
5712e37437 Merge pull request #840 from dhrp/just-fixed-some-links
Fixed some links. Closes #839 #838 #835
2013-06-10 13:51:43 -07:00
Francisco Souza
b1ed75078e docs/api/remote: fix rst syntax in the "Search images" section 2013-06-10 16:07:57 -03:00
Joffrey F
47d7486bbe Merge pull request #814 from dotcloud/708-pushpull-multislash
Support for special namespace 'src' (highland support)
2013-06-10 11:29:39 -07:00
shin-
d227af1edd Escape remote names on repo push/pull 2013-06-10 11:28:27 -07:00
shin-
4e18010731 Support for special namespace 'src' (highland support) 2013-06-10 11:28:26 -07:00
shin-
db3242e4bb Send X-Docker-Endpoints header when validating the images upload with the index at the end of a push 2013-06-10 11:21:56 -07:00
Guillaume J. Charmes
7169212683 Fix typo 2013-06-10 11:08:40 -07:00
Guillaume J. Charmes
2a6a1d439c Merge pull request #867 from Turbo87/patch-1
Fixed broken link in README
2013-06-10 10:57:09 -07:00
Guillaume J. Charmes
8984aef899 Fix typo in docs 2013-06-10 09:32:31 -07:00
Guillaume J. Charmes
b103ac70bf Allow multiple tab/spaces between instructions and arguments 2013-06-10 09:31:59 -07:00
Tobias Bieniek
37c20fa64b Fixed broken link in README 2013-06-10 19:03:54 +03:00
Victor Vieux
ab0d0a28a8 fix errors when no body 2013-06-10 15:06:52 +00:00
Victor Vieux
0de3f1ca9a add tests 2013-06-10 14:14:54 +00:00
Victor Vieux
95d66ebc6b specify public port 2013-06-10 13:56:43 +00:00
Michael Crosby
393e873d25 Add Access-Control-Allow-Methods header
Add the Access-Control-Allow-Methods header so that
DELETE operations are allowed.

Also move the write CORS headers method before
docker writes a 404 not found so that the client
receives the correct response and not an invalid
CORS request.
2013-06-09 17:17:35 -09:00
Guillaume J. Charmes
956491f853 Merge pull request #855 from samjsharpe/fix_missing_hyphen
Build from Dockerfile on stdin requires a hyphen
2013-06-07 13:04:27 -07:00
Calen Pennington
302660e362 Add an option to forward all ports to the vagrant host that have been exported from docker containers 2013-06-07 15:56:39 -04:00
Sam J Sharpe
5e6cd21f8b Build from Dockerfile on stdin requires a hypen
There is a missing hypen in the documentation:
    `docker build < Dockerfile` will complain
    `docker build - < Dockerfile` will not complain
2013-06-07 20:35:34 +01:00
Ken Cochrane
9e1cd37bbc seperated the registry and index API's into their own docs
seperated the registry and index API's into their own docs and merged
in the index search api into the index api. Also renamed the original
registry api to registry_index_spec.
2013-06-07 13:42:52 -04:00
shin-
8d4282cd36 Remove CR/NL from description in docker CLI. Also moved description shortening to the client 2013-06-07 06:09:24 -07:00
Guillaume J. Charmes
1e0738f63f Make the progressbar human readable 2013-06-06 18:42:52 -07:00
Guillaume J. Charmes
f355d33b5f Make the progressbar take the image size into consideration 2013-06-06 18:16:16 -07:00
Solomon Hykes
968e08a9ba Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-06-07 02:41:08 +02:00
Guillaume J. Charmes
2cc22de696 Update documentation for docker build 2013-06-06 16:48:36 -07:00
Guillaume J. Charmes
12c9b9b3c9 Implement build from git 2013-06-06 16:41:41 -07:00
Guillaume J. Charmes
a11e61677c Move the docker build URL form client to server, prepare for GIT support 2013-06-06 16:09:46 -07:00
Guillaume J. Charmes
01f446e908 Allow to docker build URL 2013-06-06 15:56:09 -07:00
Guillaume J. Charmes
f4a4cfd2cc Move isUrl to utils.IsURL 2013-06-06 15:50:09 -07:00
Guillaume J. Charmes
eaa2183d77 Fix issue EXPOSE override CMD within builder 2013-06-06 15:48:12 -07:00
Guillaume J. Charmes
31d2b258c1 Allow remote url to be passed to the ADD instruction within the builder 2013-06-06 15:40:46 -07:00
unclejack
4b3a381f39 docs: build: ADD copies just needed data w/ full path 2013-06-07 00:39:43 +03:00
Solomon Hykes
56473d4cce Typo in MAINTAINERS file 2013-06-06 22:52:55 +02:00
unclejack
efa7ea592c docs: warn about build data tx & ADD w/o context
This updates the documentation to mention that:
1. a lot of data may get sent to the docker daemon if there is a lot of
data in the directory passed to docker build
2. ADD doesn't work in the absence of the context
3. running without a context doesn't send file data to the docker
daemon
4. explain that the data sent to the docker daemon will be used by ADD
commands
2013-06-06 22:06:12 +03:00
Guillaume J. Charmes
afd325a884 Solve an issue with the -dns in daemon mode 2013-06-06 11:01:29 -07:00
Guillaume J. Charmes
a3f6054f97 Check for local dns server and output a warning 2013-06-06 11:01:09 -07:00
Sam Alba
da937bf214 Update README.md 2013-06-06 11:09:11 -06:00
Solomon Hykes
42b63eb818 Daniel Mizyrycki is maintainer of dockerbuilder, the official build environment for docker binary releases 2013-06-06 19:06:54 +02:00
Solomon Hykes
0d6db333d6 docker-build contrib script is deprecated by the new 'build' command 2013-06-06 19:05:21 +02:00
Solomon Hykes
3999465c85 Merge branch 'master' of ssh://github.com/dotcloud/docker 2013-06-06 19:04:17 +02:00
Daniel Mizyrycki
1cc4049e82 Merge pull request #827 from dotcloud/dev_environment_update
Update "Setting Up a Dev Environment" doc, with modern golang  PPA and stable lxc kernel
2013-06-06 09:48:19 -07:00
Victor Vieux
4107701062 add [] and move errors to stderr 2013-06-06 15:45:08 +00:00
Victor Vieux
a799cdad3e allow multiple params in inspect 2013-06-06 15:22:54 +00:00
Victor Vieux
a118ad90ed changed to 12.04 and add kernel 2013-06-06 12:36:28 +00:00
Thatcher Peskens
0f23fb949d Fixed some links
* Added Google group to FAQ on docs
* Changed IRC link
* Fixed link to contributing broken by 326faec
2013-06-05 18:06:51 -07:00
Thatcher
f1992eeea5 Merge pull request #817 from dhrp/blog-in-navigation
Modified the navigation in both website and documentaion to include the blog.
2013-06-05 17:28:19 -07:00
Guillaume J. Charmes
84d68007cb Add -dns to docker daemon 2013-06-05 14:20:54 -07:00
Victor Vieux
bf63cb9045 bump to master again 2013-06-05 16:01:36 +00:00
Victor Vieux
ce0041832c bump to master 2013-06-05 15:30:45 +00:00
Solomon Hykes
97d5f525f4 hack/PRINCIPLES.md: a list of principles guiding Docker's design. The goal is to scale the decision-making in the project and remove @shykes as a bottleneck as much as possible 2013-06-05 17:27:53 +02:00
Solomon Hykes
2ea29ce0ef hack/ROADMAP.md: a high-level roadmap. Make a pull request to suggest changes 2013-06-05 17:26:26 +02:00
Solomon Hykes
068076f775 Merge pull request #822 from lopter/master
* Client: Print the container id before the hijack in `docker run` (see also #804)
2013-06-05 08:08:30 -07:00
Solomon Hykes
34c8b24211 Merge pull request #812 from dotcloud/809-progress_message-fix
* Remote API: Fix progress message in client
2013-06-05 07:27:31 -07:00
Victor Vieux
e3cc625315 update doc to newer go 2013-06-05 13:19:49 +00:00
Victor Vieux
f67ea78cce move xino stuff to /dev/shm 2013-06-05 12:59:05 +00:00
Victor Vieux
6255112926 updated doc 2013-06-05 13:19:57 +02:00
Victor Vieux
c906239220 bump to master 2013-06-05 10:23:45 +00:00
Victor Vieux
b4682e6707 bump to master 2013-06-05 10:19:51 +00:00
Solomon Hykes
04050c4173 Merge pull request #818 from johncosta/ubuntu-1304-add-apt-repository
Remove provider specifc language
2013-06-05 02:40:09 -07:00
Louis Opter
7e6ede6379 Print the container id before the hijack in docker run (see also #804)
This is useful when you want to get the container id before you start to
interact with stdin (which is what I'm doing in dotcloud/sandbox).
2013-06-04 15:32:59 -07:00
Guillaume J. Charmes
63e80384ea Fix nil pointer on some situatuion 2013-06-04 14:35:32 -07:00
Guillaume J. Charmes
7ef9833dbb Put back panic for go1.0.3 compatibility 2013-06-04 14:26:40 -07:00
Guillaume J. Charmes
c1ee9bf881 Merge pull request #808 from dotcloud/795-lintify
Cleanup source
2013-06-04 14:20:38 -07:00
John Costa
c000ef194c Remove provider specifc language 2013-06-04 16:01:38 -04:00
Ken Cochrane
479ac9afa7 Merge pull request #802 from johncosta/ubuntu-1304-add-apt-repository
- Documentation: adding missing dependency to the ubuntu linux install page.
2013-06-04 11:55:34 -07:00
Thatcher Peskens
716892b95d Modified the navigation in both website and documentaion to include the Blog. 2013-06-04 11:41:54 -07:00
Thatcher
d7a6485dfe Merge pull request #796 from dhrp/added-and-fixed-links
Added and fixed some links (closes #502)
2013-06-04 11:28:40 -07:00
Victor Vieux
fd224ee590 linted names 2013-06-04 18:00:22 +00:00
Victor Vieux
3922691fb9 fix progress message in client 2013-06-04 16:09:08 +00:00
Sam Alba
c566c8efc7 Merge pull request #810 from dotcloud/proxy-fix
fix regression on proxy
2013-06-04 08:46:40 -07:00
Victor Vieux
06b585ce8a fix proxy 2013-06-04 15:44:27 +00:00
John Costa
e61af8bc62 some installations of ubuntu 13.04 (digital ocean Ubuntu 13.04 x64 Server in this case) require software-properties-common to be installed before being able to use add-apt-repository 2013-06-04 10:40:44 -04:00
Victor Vieux
b6825f98c0 bump to master 2013-06-04 14:00:18 +00:00
Victor Vieux
86ada2fa5d drop/omit 2013-06-04 13:51:12 +00:00
Victor Vieux
b515a5a9ec go vet 2013-06-04 13:24:58 +00:00
Michael Crosby
6d5bdff394 Add flag to enable cross domain requests in Api
Add the -api-enable-cors flag when running docker
in daemon mode to allow CORS requests to be made to
the Remote Api.  The default value is false for this
flag to not allow cross origin request to be made.

Also added a handler for OPTIONS requests the standard
for cross domain requests is to initially make an
OPTIONS request to the api.
2013-06-03 21:39:00 -04:00
Guillaume J. Charmes
0ca8844398 Fix stale command with stdout is not allocated 2013-06-03 17:39:29 -07:00
Sam Alba
10ef4f7f39 Merge pull request #797 from dotcloud/registry-fix-missing-body-close
registry.go: Fixed missing Body.Close()
2013-06-03 14:43:50 -07:00
Sam Alba
cff3b37a61 Disabled HTTP keep-alive in the default HTTP client for Registry calls 2013-06-03 14:42:21 -07:00
Victor Vieux
d26a3b37a6 allow docker run <name>:<id> 2013-06-03 20:00:15 +00:00
Guillaume J. Charmes
82dd963e08 Minor changes in registry.go 2013-06-03 12:20:52 -07:00
Sam Alba
830c458fe7 Fixed missing Body.Close when doing some HTTP requests. It should improve some request issues. 2013-06-03 12:14:57 -07:00
Thatcher Peskens
38f29f7d0c Changed some text on the website to include docker-club
Fixed a link in the FAQ on the docs
Fixed a detail in the Makefile for pushing the site to dotcloud
2013-06-03 11:45:19 -07:00
Solomon Hykes
a8ae398bf5 Bumped version to 0.4.0 2013-06-03 10:59:48 -07:00
Victor Vieux
7e59b83053 removed auth in pull 2013-06-03 17:51:52 +00:00
Guillaume J. Charmes
7a4408f608 Merge pull request #794 from dotcloud/780-diff-fix2
- Runtime: remove TrimLeft as it's go1.1
2013-06-03 10:41:08 -07:00
Victor Vieux
854039b6ba remove TrimLeft as it's go1.1 2013-06-03 17:25:51 +00:00
Guillaume J. Charmes
070923b14f Merge pull request #792 from dotcloud/780-diff-fix
- Runtime: fix Path corruption in 'docker diff'
2013-06-03 10:06:25 -07:00
Victor Vieux
71b1657e8d added test 2013-06-03 17:02:57 +00:00
Guillaume J. Charmes
1bafe9da26 Merge pull request #793 from dotcloud/774-remove_login_check_on_pull-fix
- Registry: remove login check on pull
2013-06-03 09:37:02 -07:00
Victor Vieux
1ce4ba6c9f remove check on login 2013-06-03 15:33:29 +00:00
Victor Vieux
a55a0d370d ([a-z0-9_]{4,30})/([a-zA-Z0-9-_.]+) 2013-06-03 14:23:57 +00:00
Guillaume J. Charmes
2b1b3c1270 Merge pull request #784 from dotcloud/remove_cgo_dependency
* Runtime: Remove cgo dependency
2013-06-03 07:03:17 -07:00
Guillaume J. Charmes
8243f2510e Update test to reflect new ApiInfo struct 2013-06-03 06:44:00 -07:00
Guillaume J. Charmes
0443cc351d Merge pull request #772 from dotcloud/improve_version_info_cmds
* API: Improve version info cmds
2013-06-03 06:36:09 -07:00
Victor Vieux
ca902b6be4 bump master 2013-06-03 12:37:51 +00:00
Victor Vieux
844a8db6c6 add debug 2013-06-03 12:21:22 +00:00
Victor Vieux
3dd1e4d58c added docs and moved to api version 1.2 2013-06-03 12:09:16 +00:00
Victor Vieux
62c78696cd bump to master 2013-06-03 11:06:13 +00:00
Victor Vieux
e16c93486d fix Path corruption in 'docker diff' 2013-06-03 10:19:20 +00:00
Solomon Hykes
e42eb7fa8c Meta: added Guillaume as primary maintainer for tty code 2013-06-02 23:42:18 -07:00
Solomon Hykes
cebfde9ea5 Merge pull request #787 from gasi/nodejs-centos-docs
* Documentation: Deploying a Node.js Web App on CentOS
* Documentation: small formatting improvements
2013-06-02 23:27:29 -07:00
Solomon Hykes
eff7a15bea Merge pull request #785 from jweede/master
* Documentation: spelling correction in website
2013-06-02 23:14:09 -07:00
Daniel Gasienica
82dadc2005 Document installation of npm dependencies /ht @grigio 2013-06-02 20:10:22 -07:00
Guillaume J. Charmes
2d52d4d614 Merge pull request #789 from samjsharpe/fix-extra-brace
Removes a brace in the description of the wait command
2013-06-02 15:22:11 -07:00
Sam J Sharpe
ca5ae266b7 Removes a brace in the description of the wait command 2013-06-02 22:40:56 +01:00
Daniel Gasienica
464765b940 Add link at the beginning 2013-06-01 22:16:26 -07:00
Daniel Gasienica
e9ffc1e499 Add Node.js web app example using CentOS 2013-06-01 22:06:53 -07:00
Daniel Gasienica
4fb9a6eafb Use code blocks 2013-06-01 22:03:41 -07:00
Daniel Gasienica
157547845a Name examples consistently 2013-06-01 22:03:28 -07:00
Daniel Gasienica
2935ca7ee2 Use title case for consistency 2013-06-01 22:03:12 -07:00
Daniel Gasienica
23452f1573 Use em dash in title 2013-06-01 22:02:24 -07:00
Daniel Gasienica
f6f345b1fe Fix typo 2013-06-01 21:55:01 -07:00
Daniel Gasienica
e3fd61ad74 Add more tags 2013-06-01 21:27:27 -07:00
Daniel Gasienica
01ce63aacd Make style consistent 2013-06-01 21:26:58 -07:00
Daniel Gasienica
3ca9c11110 Add Mac OS X instructions for doc tools 2013-06-01 21:26:18 -07:00
Daniel Gasienica
b4df0b17af Add make server command to preview docs 2013-06-01 21:25:51 -07:00
Jon Wedaman
7f65bf508e Spelling correction in docs 2013-06-01 21:48:32 -04:00
Guillaume J. Charmes
a70dd65964 Move Termios struct to os specific file 2013-06-01 16:19:50 -07:00
Guillaume J. Charmes
3cc0963ad1 Remove unused constants 2013-06-01 15:55:52 -07:00
Guillaume J. Charmes
31eb01ae8a Use uintptr instead of int for Fd 2013-06-01 15:55:05 -07:00
Guillaume J. Charmes
64f346779f Remove cgo from term 2013-06-01 15:51:45 -07:00
Guillaume J. Charmes
078a19d725 Merge pull request #777 from dotcloud/bsdtar-env
Pass a guaranteed environment to bsdtar for predictable behaviour
2013-06-01 11:28:55 -07:00
Solomon Hykes
561ceac55d * Runtime: pass a guaranteed environment to bsdtar for predictable behavior without depending on the underlying host configuration. 2013-05-31 22:25:48 -07:00
Guillaume J. Charmes
a373c770b6 Merge pull request #707 from unclejack/411-add-arch-field
411 add architecture field
2013-05-31 17:26:11 -07:00
Solomon Hykes
90b8c5ce67 Merge pull request #762 from dotcloud/761-packaging-dockerbuilder
Packaging: Ensure dockerbuilder can build docker PPA
2013-05-31 16:28:44 -07:00
Guillaume J. Charmes
9bc71c101c Merge pull request #719 from dotcloud/json_stream-feature
* API: push, pull, import, insert -> Json Stream
2013-05-31 16:05:15 -07:00
Guillaume J. Charmes
f41d2ec4d9 Update api docs 2013-05-31 15:56:30 -07:00
Guillaume J. Charmes
1dae7a25b9 Improve the docker version and docker info commands 2013-05-31 15:53:57 -07:00
Daniel Mizyrycki
926c1d45aa Merge pull request #533 from tianon/mkimage-debian
Update mkimage-debian.sh now that wheezy is officially the stable release
MD: Ref issue #447
2013-05-31 14:53:37 -07:00
Guillaume J. Charmes
80b8756da3 Merge pull request #727 from dotcloud/remove_hijack_logs-feature
* API: remove hijack on the client in logs, and split stdout / stderr
2013-05-31 14:43:59 -07:00
Guillaume J. Charmes
7d167590bc Merge pull request #766 from dotcloud/prevent_attach_stopped_container-feature
returns an error if the container we want to attach is not running
2013-05-31 14:41:57 -07:00
Solomon Hykes
76bb920449 Merge pull request #753 from dotcloud/registry_api_remove_teams
Remove teams from the registry API
2013-05-31 12:41:12 -07:00
Solomon Hykes
1ac36a3adf Merge pull request #767 from gasi/master
Fix minor documentation error in ‘Running Redis Service’ example
2013-05-31 12:24:25 -07:00
Solomon Hykes
46bdbbabba Merge pull request #755 from dotcloud/infrastructure2
+ Meta: organize the project infrastructure: servers, dns, email etc.
2013-05-31 12:23:55 -07:00
Daniel Gasienica
766a2db0d9 Add Daniel Gasienica to AUTHORS 2013-05-31 12:19:57 -07:00
Daniel Gasienica
fd0c501e6d Fix minor documentation error in ‘Running Redis Service’ example 2013-05-31 12:19:49 -07:00
Victor Vieux
468e4c4b56 returns an error if the container we want to attach is not running 2013-05-31 15:34:23 +00:00
Guillaume J. Charmes
9eda9154a7 Merge pull request #764 from dotcloud/add_t_parameter_doc_build-fix
add missing -t parameter in the doc
2013-05-31 07:49:29 -07:00
Victor Vieux
2baea24879 add -t parameter in the doc 2013-05-31 14:40:09 +00:00
Victor Vieux
9060b5c2f5 added proper returns type, move the auto-prune in v1.1 api 2013-05-31 14:37:02 +00:00
Daniel Mizyrycki
1040225e36 Packaging: Ensure dockerbuilder can build docker PPA 2013-05-31 00:59:18 -07:00
Andy Rothfusz
1c091657d4 Merge pull request #745 from rogaha/doc_ssh_service_example_uptd
Add screencast summary. Makes it easy to reproduce steps.
2013-05-30 18:49:32 -07:00
Victor Vieux
3afdd82e42 bump to master 2013-05-30 23:38:40 +00:00
Victor Vieux
bd38b47552 bump to master 2013-05-30 23:32:57 +00:00
Victor Vieux
5aa95b667c WIP needs to fix HTTP error codes 2013-05-30 22:53:45 +00:00
Guillaume J. Charmes
054451fd19 NON-WORKING: Beginning of rmi refactor 2013-05-30 12:30:21 -07:00
Solomon Hykes
43f369ea0c Organize the project infrastructure: servers, dns, email etc. 2013-05-30 12:28:24 -07:00
Solomon Hykes
531b30119a Remove special case for 'teams' from registry api 2013-05-30 11:37:58 -07:00
Victor Vieux
cd002a4d16 ensure progress downloader is well formated 2013-05-30 17:00:42 +00:00
Victor Vieux
49e656839f move auth to the client WIP 2013-05-30 15:39:43 +00:00
Roberto Gandolfo Hashioka
c7af917d13 updated the running ssh service example with the video's transcription 2013-05-29 11:29:30 -07:00
Guillaume J. Charmes
c05e9f856d Error output fix for docker rmi 2013-05-29 11:11:19 -07:00
Victor Vieux
7e92302c4f auto prune WIP 2013-05-29 17:27:32 +00:00
Victor Vieux
94f0d478de refacto 2013-05-29 17:01:54 +00:00
Victor Vieux
2eb4e2a0b8 removed the -f 2013-05-29 16:31:47 +00:00
Victor Vieux
f339fc2eb9 bump to master 2013-05-29 13:52:18 +00:00
Victor Vieux
ea9095c562 merge master 2013-05-29 11:49:39 +00:00
Victor Vieux
8699805756 documentation 2013-05-28 15:49:57 +00:00
Victor Vieux
fbcd8503b3 remove hijack on the client in logs, and split stdout / stderr 2013-05-27 16:07:05 +00:00
Victor Vieux
5a36efb61f fix json encoding, and use less casts 2013-05-26 23:45:45 +00:00
Victor Vieux
14212930e4 ensure valid json 2013-05-25 15:51:26 +00:00
Victor Vieux
c8c7094b2e imporved error, push, import insert 2013-05-25 15:09:46 +00:00
Victor Vieux
cb0bc4adc2 add error handling 2013-05-25 14:12:02 +00:00
unclejack
5f69a53dba set architecture to x86_64 by default
We're going to hardcode architecture to amd64 for now.
This is a stub and will have to be changed to set the actual arch.
2013-05-25 13:03:49 +03:00
unclejack
2cf92abf0e add arch field to image struct 2013-05-24 21:41:30 +03:00
Victor Vieux
1c946ef003 fix: Can't lookup root of unregistered image 2013-05-24 13:03:09 +00:00
Victor Vieux
b45143da9b switch to SI standard and add test 2013-05-23 10:29:09 +00:00
Victor Vieux
ed56b6a905 fix typo 2013-05-23 09:35:20 +00:00
Victor Vieux
6fce89e60b bump to master 2013-05-22 13:41:29 +00:00
Victor Vieux
4489005cb2 add regexp check on repo's name 2013-05-21 12:53:05 +00:00
Victor Vieux
67b20f2c8c add check to see if the image isn't parent of another and add -f to force 2013-05-20 18:31:45 +00:00
Victor Vieux
6102552d61 Merge branch 'master' into 610-improve_rmi-feature 2013-05-18 14:29:37 +00:00
Victor Vieux
d7673274d2 check if the image to delete isn't parent of another 2013-05-18 14:29:32 +00:00
Victor Vieux
f01990aad2 fix 2013-05-17 17:57:44 +00:00
Guillaume J. Charmes
db1e965b65 Merge fixes + cleanup 2013-05-16 11:27:50 -07:00
Guillaume J. Charmes
2ae8aaa106 Merge branch 'master' into 610-improve_rmi-feature 2013-05-16 11:15:16 -07:00
Victor Vieux
c80448c4d1 improve rmi 2013-05-15 13:51:50 +00:00
Victor Vieux
c75942c79d add 4243 port forward 2013-05-15 02:41:19 +02:00
Victor Vieux
a91b710961 add sizes in images and containers 2013-05-13 15:14:20 +02:00
Tianon Gravi
2f89315bf8 Update mkimage-debian.sh now that wheezy is officially the stable release - also, we can't rely on "release" versions for testing or unstable - only "stable" has reliable release versions 2013-05-06 17:00:21 -06:00
120 changed files with 9408 additions and 3969 deletions

View File

@@ -19,3 +19,7 @@ Andy Smith <github@anarkystic.com>
<victor.vieux@dotcloud.com> <dev@vvieux.com>
<dominik@honnef.co> <dominikh@fork-bomb.org>
Thatcher Peskens <thatcher@dotcloud.com>
<ehanchrow@ine.com> <eric.hanchrow@gmail.com>
Walter Stanish <walter@pratyeka.org>
<daniel@gasienica.ch> <dgasienica@zynga.com>
Roberto Hashioka <roberto_hashioka@hotmail.com>

25
AUTHORS
View File

@@ -6,6 +6,8 @@
Al Tobey <al@ooyala.com>
Alexey Shamrin <shamrin@gmail.com>
Andrea Luzzardi <aluzzardi@gmail.com>
Andreas Tiefenthaler <at@an-ti.eu>
Andrew Munsell <andrew@wizardapps.net>
Andy Rothfusz <github@metaliveblog.com>
Andy Smith <github@anarkystic.com>
Antony Messerli <amesserl@rackspace.com>
@@ -14,18 +16,24 @@ Brandon Liu <bdon@bdon.org>
Brian McCallister <brianm@skife.org>
Bruno Bigras <bigras.bruno@gmail.com>
Caleb Spare <cespare@gmail.com>
Calen Pennington <cale@edx.org>
Charles Hooper <charles.hooper@dotcloud.com>
Christopher Currie <codemonkey+github@gmail.com>
Daniel Gasienica <daniel@gasienica.ch>
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
Daniel Robinson <gottagetmac@gmail.com>
Daniel Von Fange <daniel@leancoder.com>
Dominik Honnef <dominik@honnef.co>
Don Spaulding <donspauldingii@gmail.com>
Dr Nic Williams <drnicwilliams@gmail.com>
Elias Probst <mail@eliasprobst.eu>
Eric Hanchrow <ehanchrow@ine.com>
Evan Wies <evan@neomantra.net>
ezbercih <cem.ezberci@gmail.com>
Flavio Castelli <fcastelli@suse.com>
Francisco Souza <f@souza.cc>
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
Gareth Rushgrove <gareth@morethanseven.net>
Guillaume J. Charmes <guillaume.charmes@dotcloud.com>
Harley Laue <losinggeneration@gmail.com>
Hunter Blanks <hunter@twilio.com>
@@ -33,14 +41,22 @@ Jeff Lindsay <progrium@gmail.com>
Jeremy Grosser <jeremy@synack.me>
Joffrey F <joffrey@dotcloud.com>
John Costa <john.costa@gmail.com>
Jon Wedaman <jweede@gmail.com>
Jonas Pfenniger <jonas@pfenniger.name>
Jonathan Rudenberg <jonathan@titanous.com>
Joseph Anthony Pasquale Holsten <joseph@josephholsten.com>
Julien Barbier <write0@gmail.com>
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
Ken Cochrane <kencochrane@gmail.com>
Kevin J. Lynagh <kevin@keminglabs.com>
kim0 <email.ahmedkamal@googlemail.com>
Kiran Gangadharan <kiran.daredevil@gmail.com>
Louis Opter <kalessin@kalessin.fr>
Marcus Farkas <toothlessgear@finitebox.com>
Mark McGranaghan <mmcgrana@gmail.com>
Maxim Treskin <zerthurd@gmail.com>
meejah <meejah@meejah.ca>
Michael Crosby <crosby.michael@gmail.com>
Mikhail Sobolev <mss@mawhrin.net>
Nate Jones <nate@endot.org>
Nelson Chen <crazysim@gmail.com>
@@ -48,18 +64,25 @@ Niall O'Higgins <niallo@unworkable.org>
odk- <github@odkurzacz.org>
Paul Bowsher <pbowsher@globalpersonals.co.uk>
Paul Hammond <paul@paulhammond.org>
Phil Spitler <pspitler@gmail.com>
Piotr Bogdan <ppbogdan@gmail.com>
Renato Riccieri Santos Zannon <renato.riccieri@gmail.com>
Robert Obryk <robryk@gmail.com>
Roberto Hashioka <roberto_hashioka@hotmail.com>
Sam Alba <sam.alba@gmail.com>
Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
Shawn Siefkas <shawn.siefkas@meredith.com>
Silas Sewell <silas@sewell.org>
Solomon Hykes <solomon@dotcloud.com>
Sridhar Ratnakumar <sridharr@activestate.com>
Thatcher Peskens <thatcher@dotcloud.com>
Thomas Bikeev <thomas.bikeev@mac.com>
Thomas Hansen <thomas.hansen@gmail.com>
Tianon Gravi <admwiggin@gmail.com>
Tim Terhorst <mynamewastaken+git@gmail.com>
Troy Howard <thoward37@gmail.com>
Tobias Bieniek <Tobias.Bieniek@gmx.de>
unclejack <unclejacksons@gmail.com>
Victor Vieux <victor.vieux@dotcloud.com>
Vivek Agarwal <me@vivek.im>
Walter Stanish <walter@pratyeka.org>
Will Dietz <w@wdtz.org>

View File

@@ -1,5 +1,60 @@
# Changelog
## 0.4.5 (2013-06-21)
+ Builder: 'docker build git://URL' fetches and builds a remote git repository
* Runtime: 'docker ps -s' optionally prints container size
* Tests: Improved and simplified
- Runtime: fix a regression introduced in 0.4.3 which caused the logs command to fail.
- Builder: fix a regression when using ADD with single regular file.
## 0.4.4 (2013-06-19)
- Builder: fix a regression introduced in 0.4.3 which caused builds to fail on new clients.
## 0.4.3 (2013-06-19)
+ Builder: ADD of a local file will detect tar archives and unpack them
* Runtime: Remove bsdtar dependency
* Runtime: Add unix socket and multiple -H support
* Runtime: Prevent rm of running containers
* Runtime: Use go1.1 cookiejar
* Builder: ADD improvements: use tar for copy + automatically unpack local archives
* Builder: ADD uses tar/untar for copies instead of calling 'cp -ar'
* Builder: nicer output for 'docker build'
* Builder: fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented.
* Client: HumanReadable ProgressBar sizes in pull
* Client: Fix docker version's git commit output
* API: Send all tags on History API call
* API: Add tag lookup to history command. Fixes #882
- Runtime: Fix issue detaching from running TTY container
- Runtime: Forbid parralel push/pull for a single image/repo. Fixes #311
- Runtime: Fix race condition within Run command when attaching.
- Builder: fix a bug which caused builds to fail if ADD was the first command
- Documentation: fix missing command in irc bouncer example
## 0.4.2 (2013-06-17)
- Packaging: Bumped version to work around an Ubuntu bug
## 0.4.1 (2013-06-17)
+ Remote Api: Add flag to enable cross domain requests
+ Remote Api/Client: Add images and containers sizes in docker ps and docker images
+ Runtime: Configure dns configuration host-wide with 'docker -d -dns'
+ Runtime: Detect faulty DNS configuration and replace it with a public default
+ Runtime: allow docker run <name>:<id>
+ Runtime: you can now specify public port (ex: -p 80:4500)
* Client: allow multiple params in inspect
* Client: Print the container id before the hijack in `docker run`
* Registry: add regexp check on repo's name
* Registry: Move auth to the client
* Runtime: improved image removal to garbage-collect unreferenced parents
* Vagrantfile: Add the rest api port to vagrantfile's port_forward
* Upgrade to Go 1.1
- Builder: don't ignore last line in Dockerfile when it doesn't end with \n
- Registry: Remove login check on pull
## 0.4.0 (2013-06-03)
+ Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
+ Introducing Remote API: control Docker programmatically using a simple HTTP/json API
* Runtime: various reliability and usability improvements
## 0.3.4 (2013-05-30)
+ Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
+ Builder: 'docker build -t FOO' applies the tag FOO to the newly built container.

36
FIXME Normal file
View File

@@ -0,0 +1,36 @@
## FIXME
This file is a loose collection of things to improve in the codebase, for the internal
use of the maintainers.
They are not big enough to be in the roadmap, not user-facing enough to be github issues,
and not important enough to be discussed in the mailing list.
They are just like FIXME comments in the source code, except we're not sure where in the source
to put them - so we put them here :)
* Merge Runtime, Server and Builder into Runtime
* Run linter on codebase
* Unify build commands and regular commands
* Move source code into src/ subdir for clarity
* Clean up the Makefile, it's a mess
* docker build: on non-existent local path for ADD, don't show full absolute path on the host
* mount into /dockerinit rather than /sbin/init
* docker tag foo REPO:TAG
* use size header for progress bar in pull
* Clean up context upload in build!!!
* Parallel pull
* Ensure /proc/sys/net/ipv4/ip_forward is 1
* Force DNS to public!
* Always generate a resolv.conf per container, to avoid changing resolv.conf under thne container's feet
* Save metadata with import/export
* Upgrade dockerd without stopping containers
* bring back git revision info, looks like it was lost
* Simple command to remove all untagged images
* Simple command to clean up containers for disk space
* Caching after an ADD
* entry point config
* bring back git revision info, looks like it was lost
* Clean up the ProgressReader api, it's a PITA to use

View File

@@ -17,7 +17,7 @@ endif
GIT_COMMIT = $(shell git rev-parse --short HEAD)
GIT_STATUS = $(shell test -n "`git status --porcelain`" && echo "+CHANGES")
BUILD_OPTIONS = -ldflags "-X main.GIT_COMMIT $(GIT_COMMIT)$(GIT_STATUS)"
BUILD_OPTIONS = -a -ldflags "-X main.GITCOMMIT $(GIT_COMMIT)$(GIT_STATUS) -d -w"
SRC_DIR := $(GOPATH)/src
@@ -33,7 +33,7 @@ all: $(DOCKER_BIN)
$(DOCKER_BIN): $(DOCKER_DIR)
@mkdir -p $(dir $@)
@(cd $(DOCKER_MAIN); go build $(GO_OPTIONS) $(BUILD_OPTIONS) -o $@)
@(cd $(DOCKER_MAIN); CGO_ENABLED=0 go build $(GO_OPTIONS) $(BUILD_OPTIONS) -o $@)
@echo $(DOCKER_BIN_RELATIVE) is created.
$(DOCKER_DIR):
@@ -46,6 +46,7 @@ whichrelease:
release: $(BINRELEASE)
s3cmd -P put $(BINRELEASE) s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-$(RELEASE_VERSION).tgz
s3cmd -P put docker-latest.tgz s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-latest.tgz
srcrelease: $(SRCRELEASE)
deps: $(DOCKER_DIR)
@@ -60,6 +61,7 @@ $(SRCRELEASE):
$(BINRELEASE): $(SRCRELEASE)
rm -f $(BINRELEASE)
cd $(SRCRELEASE); make; cp -R bin docker-$(RELEASE_VERSION); tar -f ../$(BINRELEASE) -zv -c docker-$(RELEASE_VERSION)
cd $(SRCRELEASE); cp -R bin docker-latest; tar -f ../docker-latest.tgz -zv -c docker-latest
clean:
@rm -rf $(dir $(DOCKER_BIN))
@@ -72,6 +74,9 @@ endif
test: all
@(cd $(DOCKER_DIR); sudo -E go test $(GO_OPTIONS))
testall: all
@(cd $(DOCKER_DIR); sudo -E go test ./... $(GO_OPTIONS))
fmt:
@gofmt -s -l -w .

7
NOTICE
View File

@@ -3,4 +3,9 @@ Copyright 2012-2013 dotCloud, inc.
This product includes software developed at dotCloud, inc. (http://www.dotcloud.com).
This product contains software (https://github.com/kr/pty) developed by Keith Rarick, licensed under the MIT License.
This product contains software (https://github.com/kr/pty) developed by Keith Rarick, licensed under the MIT License.
Transfers of Docker shall be in accordance with applicable export controls of any country and all other applicable
legal requirements. Docker shall not be distributed or downloaded to or in Cuba, Iran, North Korea, Sudan or Syria
and shall not be distributed or downloaded to any person on the Denied Persons List administered by the U.S.
Department of Commerce.

View File

@@ -97,7 +97,7 @@ Quick install on Ubuntu 12.04 and 12.10
---------------------------------------
```bash
curl get.docker.io | sh -x
curl get.docker.io | sudo sh -x
```
Binary installs
@@ -108,7 +108,7 @@ Note that some methods are community contributions and not yet officially suppor
* [Ubuntu 12.04 and 12.10 (officially supported)](http://docs.docker.io/en/latest/installation/ubuntulinux/)
* [Arch Linux](http://docs.docker.io/en/latest/installation/archlinux/)
* [MacOS X (with Vagrant)](http://docs.docker.io/en/latest/installation/macos/)
* [Mac OS X (with Vagrant)](http://docs.docker.io/en/latest/installation/vagrant/)
* [Windows (with Vagrant)](http://docs.docker.io/en/latest/installation/windows/)
* [Amazon EC2 (with Vagrant)](http://docs.docker.io/en/latest/installation/amazon/)
@@ -181,7 +181,7 @@ Running an irc bouncer
----------------------
```bash
BOUNCER_ID=$(docker run -d -p 6667 -u irc shykes/znc $USER $PASSWORD)
BOUNCER_ID=$(docker run -d -p 6667 -u irc shykes/znc zncrun $USER $PASSWORD)
echo "Configure your irc client to connect to port $(docker port $BOUNCER_ID 6667) of this machine"
```
@@ -216,7 +216,8 @@ PORT=$(docker port $JOB 4444)
# Connect to the public port via the host's public address
# Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work.
IP=$(ifconfig eth0 | perl -n -e 'if (m/inet addr:([\d\.]+)/g) { print $1 }')
# Replace *eth0* according to your local interface name.
IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }')
echo hello world | nc $IP $PORT
# Verify that the network connection worked
@@ -251,7 +252,7 @@ Note
----
We also keep the documentation in this repository. The website documentation is generated using sphinx using these sources.
Please find it under docs/sources/ and read more about it https://github.com/dotcloud/docker/master/docs/README.md
Please find it under docs/sources/ and read more about it https://github.com/dotcloud/docker/tree/master/docs/README.md
Please feel free to fix / update the documentation and send us pull requests. More tutorials are also welcome.
@@ -262,14 +263,14 @@ Setting up a dev environment
Instructions that have been verified to work on Ubuntu 12.10,
```bash
sudo apt-get -y install lxc wget bsdtar curl golang git
sudo apt-get -y install lxc curl xz-utils golang git
export GOPATH=~/go/
export PATH=$GOPATH/bin:$PATH
mkdir -p $GOPATH/src/github.com/dotcloud
cd $GOPATH/src/github.com/dotcloud
git clone git@github.com:dotcloud/docker.git
git clone https://github.com/dotcloud/docker.git
cd docker
go get -v github.com/dotcloud/docker/...
@@ -371,4 +372,10 @@ Standard Container Specification
#### Security
### Legal
Transfers of Docker shall be in accordance with applicable export controls of any country and all other applicable
legal requirements. Docker shall not be distributed or downloaded to or in Cuba, Iran, North Korea, Sudan or Syria
and shall not be distributed or downloaded to any person on the Denied Persons List administered by the U.S.
Department of Commerce.

24
Vagrantfile vendored
View File

@@ -3,13 +3,16 @@
BOX_NAME = ENV['BOX_NAME'] || "ubuntu"
BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box"
VF_BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64_vmware_fusion.box"
AWS_REGION = ENV['AWS_REGION'] || "us-east-1"
AWS_AMI = ENV['AWS_AMI'] || "ami-d0f89fb9"
FORWARD_DOCKER_PORTS = ENV['FORWARD_DOCKER_PORTS']
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
config.vm.forward_port 4243, 4243
# Provision docker and new kernel if deployment was not done
if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/default/*/id").empty?
@@ -65,8 +68,29 @@ Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
rs.image = /Ubuntu/
end
config.vm.provider :vmware_fusion do |f, override|
override.vm.box = BOX_NAME
override.vm.box_url = VF_BOX_URI
override.vm.synced_folder ".", "/vagrant", disabled: true
f.vmx["displayName"] = "docker"
end
config.vm.provider :virtualbox do |vb|
config.vm.box = BOX_NAME
config.vm.box_url = BOX_URI
end
end
if !FORWARD_DOCKER_PORTS.nil?
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
(49000..49900).each do |port|
config.vm.forward_port port, port
end
end
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
(49000..49900).each do |port|
config.vm.network :forwarded_port, :host => port, :guest => port
end
end
end

358
api.go
View File

@@ -7,13 +7,19 @@ import (
"github.com/dotcloud/docker/utils"
"github.com/gorilla/mux"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"os/exec"
"strconv"
"strings"
)
const API_VERSION = 1.1
const APIVERSION = 1.3
const DEFAULTHTTPHOST string = "127.0.0.1"
const DEFAULTHTTPPORT int = 4243
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
conn, _, err := w.(http.Hijacker).Hijack()
@@ -45,30 +51,40 @@ func httpError(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusNotFound)
} else if strings.HasPrefix(err.Error(), "Bad parameter") {
http.Error(w, err.Error(), http.StatusBadRequest)
} else if strings.HasPrefix(err.Error(), "Conflict") {
http.Error(w, err.Error(), http.StatusConflict)
} else if strings.HasPrefix(err.Error(), "Impossible") {
http.Error(w, err.Error(), http.StatusNotAcceptable)
} else if strings.HasPrefix(err.Error(), "Wrong login/password") {
http.Error(w, err.Error(), http.StatusUnauthorized)
} else if strings.Contains(err.Error(), "hasn't been activated") {
http.Error(w, err.Error(), http.StatusForbidden)
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func writeJson(w http.ResponseWriter, b []byte) {
func writeJSON(w http.ResponseWriter, b []byte) {
w.Header().Set("Content-Type", "application/json")
w.Write(b)
}
// FIXME: Use stvconv.ParseBool() instead?
func getBoolParam(value string) (bool, error) {
if value == "1" || strings.ToLower(value) == "true" {
return true, nil
}
if value == "" || value == "0" || strings.ToLower(value) == "false" {
if value == "" {
return false, nil
}
return false, fmt.Errorf("Bad parameter")
ret, err := strconv.ParseBool(value)
if err != nil {
return false, fmt.Errorf("Bad parameter")
}
return ret, nil
}
func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
// FIXME: Handle multiple login at once
// FIXME: return specific error code if config file missing?
if version > 1.1 {
w.WriteHeader(http.StatusNotFound)
return nil
}
authConfig, err := auth.LoadConfig(srv.runtime.root)
if err != nil {
if err != auth.ErrConfigFileMissing {
@@ -80,40 +96,45 @@ func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
// FIXME: Handle multiple login at once
config := &auth.AuthConfig{}
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
return err
}
authConfig, err := auth.LoadConfig(srv.runtime.root)
if err != nil {
if err != auth.ErrConfigFileMissing {
return err
}
authConfig = &auth.AuthConfig{}
}
if config.Username == authConfig.Username {
config.Password = authConfig.Password
}
newAuthConfig := auth.NewAuthConfig(config.Username, config.Password, config.Email, srv.runtime.root)
status, err := auth.Login(newAuthConfig)
authConfig := &auth.AuthConfig{}
err := json.NewDecoder(r.Body).Decode(authConfig)
if err != nil {
return err
}
if status != "" {
b, err := json.Marshal(&ApiAuth{Status: status})
status := ""
if version > 1.1 {
status, err = auth.Login(authConfig, false)
if err != nil {
return err
}
writeJson(w, b)
} else {
localAuthConfig, err := auth.LoadConfig(srv.runtime.root)
if err != nil {
if err != auth.ErrConfigFileMissing {
return err
}
}
if authConfig.Username == localAuthConfig.Username {
authConfig.Password = localAuthConfig.Password
}
newAuthConfig := auth.NewAuthConfig(authConfig.Username, authConfig.Password, authConfig.Email, srv.runtime.root)
status, err = auth.Login(newAuthConfig, true)
if err != nil {
return err
}
}
if status != "" {
b, err := json.Marshal(&APIAuth{Status: status})
if err != nil {
return err
}
writeJSON(w, b)
return nil
}
w.WriteHeader(http.StatusNoContent)
@@ -126,7 +147,7 @@ func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Req
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@@ -155,7 +176,7 @@ func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r
return nil
}
func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}
@@ -174,7 +195,7 @@ func getImagesJson(srv *Server, version float64, w http.ResponseWriter, r *http.
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@@ -191,7 +212,7 @@ func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@@ -208,7 +229,7 @@ func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *ht
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@@ -225,11 +246,11 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}
@@ -237,6 +258,10 @@ func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *h
if err != nil {
return err
}
size, err := getBoolParam(r.Form.Get("size"))
if err != nil {
return err
}
since := r.Form.Get("since")
before := r.Form.Get("before")
n, err := strconv.Atoi(r.Form.Get("limit"))
@@ -244,12 +269,12 @@ func getContainersJson(srv *Server, version float64, w http.ResponseWriter, r *h
n = -1
}
outs := srv.Containers(all, n, since, before)
outs := srv.Containers(all, size, n, since, before)
b, err := json.Marshal(outs)
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@@ -292,12 +317,12 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req
if err != nil {
return err
}
b, err := json.Marshal(&ApiId{id})
b, err := json.Marshal(&APIID{id})
if err != nil {
return err
}
w.WriteHeader(http.StatusCreated)
writeJson(w, b)
writeJSON(w, b)
return nil
}
@@ -312,16 +337,25 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht
tag := r.Form.Get("tag")
repo := r.Form.Get("repo")
if version > 1.0 {
w.Header().Set("Content-Type", "application/json")
}
sf := utils.NewStreamFormatter(version > 1.0)
if image != "" { //pull
registry := r.Form.Get("registry")
if version > 1.0 {
w.Header().Set("Content-Type", "application/json")
}
if err := srv.ImagePull(image, tag, registry, w, version > 1.0); err != nil {
if err := srv.ImagePull(image, tag, registry, w, sf, &auth.AuthConfig{}); err != nil {
if sf.Used() {
w.Write(sf.FormatError(err))
return nil
}
return err
}
} else { //import
if err := srv.ImageImport(src, repo, tag, r.Body, w); err != nil {
if err := srv.ImageImport(src, repo, tag, r.Body, w, sf); err != nil {
if sf.Used() {
w.Write(sf.FormatError(err))
return nil
}
return err
}
}
@@ -342,7 +376,7 @@ func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *htt
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@@ -357,20 +391,38 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
return fmt.Errorf("Missing parameter")
}
name := vars["name"]
imgId, err := srv.ImageInsert(name, url, path, w)
if version > 1.0 {
w.Header().Set("Content-Type", "application/json")
}
sf := utils.NewStreamFormatter(version > 1.0)
imgID, err := srv.ImageInsert(name, url, path, w, sf)
if err != nil {
if sf.Used() {
w.Write(sf.FormatError(err))
return nil
}
}
b, err := json.Marshal(&APIID{ID: imgID})
if err != nil {
return err
}
b, err := json.Marshal(&ApiId{Id: imgId})
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
authConfig := &auth.AuthConfig{}
if version > 1.1 {
if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
return err
}
} else {
localAuthConfig, err := auth.LoadConfig(srv.runtime.root)
if err != nil && err != auth.ErrConfigFileMissing {
return err
}
authConfig = localAuthConfig
}
if err := parseForm(r); err != nil {
return err
}
@@ -380,8 +432,15 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
return fmt.Errorf("Missing parameter")
}
name := vars["name"]
if err := srv.ImagePush(name, registry, w); err != nil {
if version > 1.0 {
w.Header().Set("Content-Type", "application/json")
}
sf := utils.NewStreamFormatter(version > 1.0)
if err := srv.ImagePush(name, registry, w, sf, authConfig); err != nil {
if sf.Used() {
w.Write(sf.FormatError(err))
return nil
}
return err
}
return nil
@@ -389,17 +448,23 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
config := &Config{}
out := &APIRun{}
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
return err
}
if len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns() {
out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns))
config.Dns = defaultDns
}
id, err := srv.ContainerCreate(config)
if err != nil {
return err
}
out.ID = id
out := &ApiRun{
Id: id,
}
if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
out.Warnings = append(out.Warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.")
@@ -408,12 +473,13 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.")
out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.")
}
b, err := json.Marshal(out)
if err != nil {
return err
}
w.WriteHeader(http.StatusCreated)
writeJson(w, b)
writeJSON(w, b)
return nil
}
@@ -457,14 +523,30 @@ func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *ht
}
func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}
if vars == nil {
return fmt.Errorf("Missing parameter")
}
name := vars["name"]
if err := srv.ImageDelete(name); err != nil {
imgs, err := srv.ImageDelete(name, version > 1.1)
if err != nil {
return err
}
w.WriteHeader(http.StatusNoContent)
if imgs != nil {
if len(*imgs) != 0 {
b, err := json.Marshal(imgs)
if err != nil {
return err
}
writeJSON(w, b)
} else {
return fmt.Errorf("Conflict, %s wasn't deleted", name)
}
} else {
w.WriteHeader(http.StatusNoContent)
}
return nil
}
@@ -510,11 +592,11 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
if err != nil {
return err
}
b, err := json.Marshal(&ApiWait{StatusCode: status})
b, err := json.Marshal(&APIWait{StatusCode: status})
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@@ -601,7 +683,7 @@ func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
@@ -619,17 +701,17 @@ func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *htt
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
apiConfig := &ApiImageConfig{}
apiConfig := &APIImageConfig{}
if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
return err
}
image, err := srv.ImageGetCached(apiConfig.Id, apiConfig.Config)
image, err := srv.ImageGetCached(apiConfig.ID, apiConfig.Config)
if err != nil {
return err
}
@@ -637,64 +719,104 @@ func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *
w.WriteHeader(http.StatusNotFound)
return nil
}
apiId := &ApiId{Id: image.Id}
b, err := json.Marshal(apiId)
apiID := &APIID{ID: image.ID}
b, err := json.Marshal(apiID)
if err != nil {
return err
}
writeJson(w, b)
writeJSON(w, b)
return nil
}
func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := r.ParseMultipartForm(4096); err != nil {
return err
if version < 1.3 {
return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
}
remote := r.FormValue("t")
remoteURL := r.FormValue("remote")
repoName := r.FormValue("t")
tag := ""
if strings.Contains(remote, ":") {
remoteParts := strings.Split(remote, ":")
if strings.Contains(repoName, ":") {
remoteParts := strings.Split(repoName, ":")
tag = remoteParts[1]
remote = remoteParts[0]
repoName = remoteParts[0]
}
dockerfile, _, err := r.FormFile("Dockerfile")
if err != nil {
return err
}
var context io.Reader
context, _, err := r.FormFile("Context")
if err != nil {
if err != http.ErrMissingFile {
if remoteURL == "" {
context = r.Body
} else if utils.IsGIT(remoteURL) {
if !strings.HasPrefix(remoteURL, "git://") {
remoteURL = "https://" + remoteURL
}
root, err := ioutil.TempDir("", "docker-build-git")
if err != nil {
return err
}
}
defer os.RemoveAll(root)
if output, err := exec.Command("git", "clone", remoteURL, root).CombinedOutput(); err != nil {
return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
}
c, err := Tar(root, Bzip2)
if err != nil {
return err
}
context = c
} else if utils.IsURL(remoteURL) {
f, err := utils.Download(remoteURL, ioutil.Discard)
if err != nil {
return err
}
defer f.Body.Close()
dockerFile, err := ioutil.ReadAll(f.Body)
if err != nil {
return err
}
c, err := mkBuildContext(string(dockerFile), nil)
if err != nil {
return err
}
context = c
}
b := NewBuildFile(srv, utils.NewWriteFlusher(w))
if id, err := b.Build(dockerfile, context); err != nil {
id, err := b.Build(context)
if err != nil {
fmt.Fprintf(w, "Error build: %s\n", err)
} else if remote != "" {
srv.runtime.repositories.Set(remote, tag, id, false)
return err
}
if repoName != "" {
srv.runtime.repositories.Set(repoName, tag, id, false)
}
return nil
}
func ListenAndServe(addr string, srv *Server, logging bool) error {
func optionsHandler(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
w.WriteHeader(http.StatusOK)
return nil
}
func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Access-Control-Allow-Origin", "*")
w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
}
func createRouter(srv *Server, logging bool) (*mux.Router, error) {
r := mux.NewRouter()
log.Printf("Listening for HTTP on %s\n", addr)
m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{
"GET": {
"/auth": getAuth,
"/version": getVersion,
"/info": getInfo,
"/images/json": getImagesJson,
"/images/json": getImagesJSON,
"/images/viz": getImagesViz,
"/images/search": getImagesSearch,
"/images/{name:.*}/history": getImagesHistory,
"/images/{name:.*}/json": getImagesByName,
"/containers/ps": getContainersJson,
"/containers/json": getContainersJson,
"/containers/ps": getContainersJSON,
"/containers/json": getContainersJSON,
"/containers/{name:.*}/export": getContainersExport,
"/containers/{name:.*}/changes": getContainersChanges,
"/containers/{name:.*}/json": getContainersByName,
@@ -721,6 +843,9 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
"/containers/{name:.*}": deleteContainers,
"/images/{name:.*}": deleteImages,
},
"OPTIONS": {
"": optionsHandler,
},
}
for method, routes := range m {
@@ -732,6 +857,7 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
localFct := fct
f := func(w http.ResponseWriter, r *http.Request) {
utils.Debugf("Calling %s %s", localMethod, localRoute)
if logging {
log.Println(r.Method, r.RequestURI)
}
@@ -743,19 +869,47 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
}
version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
if err != nil {
version = API_VERSION
version = APIVERSION
}
if version == 0 || version > API_VERSION {
if srv.enableCors {
writeCorsHeaders(w, r)
}
if version == 0 || version > APIVERSION {
w.WriteHeader(http.StatusNotFound)
return
}
if err := localFct(srv, version, w, r, mux.Vars(r)); err != nil {
httpError(w, err)
}
}
r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
if localRoute == "" {
r.Methods(localMethod).HandlerFunc(f)
} else {
r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
}
}
}
return http.ListenAndServe(addr, r)
return r, nil
}
func ListenAndServe(proto, addr string, srv *Server, logging bool) error {
log.Printf("Listening for HTTP on %s (%s)\n", addr, proto)
r, err := createRouter(srv, logging)
if err != nil {
return err
}
l, e := net.Listen(proto, addr)
if e != nil {
return e
}
//as the daemon is launched as root, change to permission of the socket to allow non-root to connect
if proto == "unix" {
os.Chmod(addr, 0777)
}
httpSrv := http.Server{Addr: addr, Handler: r}
return httpSrv.Serve(l)
}

View File

@@ -1,71 +1,80 @@
package docker
type ApiHistory struct {
Id string
type APIHistory struct {
ID string `json:"Id"`
Tags []string `json:",omitempty"`
Created int64
CreatedBy string
CreatedBy string `json:",omitempty"`
}
type ApiImages struct {
Repository string `json:",omitempty"`
Tag string `json:",omitempty"`
Id string
Created int64
type APIImages struct {
Repository string `json:",omitempty"`
Tag string `json:",omitempty"`
ID string `json:"Id"`
Created int64
Size int64
VirtualSize int64
}
type ApiInfo struct {
Containers int
Version string
Images int
type APIInfo struct {
Debug bool
GoVersion string
NFd int `json:",omitempty"`
NGoroutines int `json:",omitempty"`
Containers int
Images int
NFd int `json:",omitempty"`
NGoroutines int `json:",omitempty"`
MemoryLimit bool `json:",omitempty"`
SwapLimit bool `json:",omitempty"`
}
type ApiContainers struct {
Id string
Image string
Command string
Created int64
Status string
Ports string
type APIRmi struct {
Deleted string `json:",omitempty"`
Untagged string `json:",omitempty"`
}
type ApiSearch struct {
type APIContainers struct {
ID string `json:"Id"`
Image string
Command string
Created int64
Status string
Ports string
SizeRw int64
SizeRootFs int64
}
type APISearch struct {
Name string
Description string
}
type ApiId struct {
Id string
type APIID struct {
ID string `json:"Id"`
}
type ApiRun struct {
Id string
Warnings []string
type APIRun struct {
ID string `json:"Id"`
Warnings []string `json:",omitempty"`
}
type ApiPort struct {
type APIPort struct {
Port string
}
type ApiVersion struct {
Version string
GitCommit string
MemoryLimit bool
SwapLimit bool
type APIVersion struct {
Version string
GitCommit string `json:",omitempty"`
GoVersion string `json:",omitempty"`
}
type ApiWait struct {
type APIWait struct {
StatusCode int
}
type ApiAuth struct {
type APIAuth struct {
Status string
}
type ApiImageConfig struct {
Id string
type APIImageConfig struct {
ID string `json:"Id"`
*Config
}

View File

@@ -6,7 +6,6 @@ import (
"bytes"
"encoding/json"
"github.com/dotcloud/docker/auth"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/utils"
"io"
"net"
@@ -18,7 +17,31 @@ import (
"time"
)
func TestGetAuth(t *testing.T) {
func TestGetBoolParam(t *testing.T) {
if ret, err := getBoolParam("true"); err != nil || !ret {
t.Fatalf("true -> true, nil | got %b %s", ret, err)
}
if ret, err := getBoolParam("True"); err != nil || !ret {
t.Fatalf("True -> true, nil | got %b %s", ret, err)
}
if ret, err := getBoolParam("1"); err != nil || !ret {
t.Fatalf("1 -> true, nil | got %b %s", ret, err)
}
if ret, err := getBoolParam(""); err != nil || ret {
t.Fatalf("\"\" -> false, nil | got %b %s", ret, err)
}
if ret, err := getBoolParam("false"); err != nil || ret {
t.Fatalf("false -> false, nil | got %b %s", ret, err)
}
if ret, err := getBoolParam("0"); err != nil || ret {
t.Fatalf("0 -> false, nil | got %b %s", ret, err)
}
if ret, err := getBoolParam("faux"); err == nil || ret {
t.Fatalf("faux -> false, err | got %b %s", ret, err)
}
}
func TestPostAuth(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
@@ -37,29 +60,23 @@ func TestGetAuth(t *testing.T) {
Email: "utest@yopmail.com",
}
authConfigJson, err := json.Marshal(authConfig)
authConfigJSON, err := json.Marshal(authConfig)
if err != nil {
t.Fatal(err)
}
req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJson))
req, err := http.NewRequest("POST", "/auth", bytes.NewReader(authConfigJSON))
if err != nil {
t.Fatal(err)
}
if err := postAuth(srv, API_VERSION, r, req, nil); err != nil {
if err := postAuth(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err)
}
if r.Code != http.StatusOK && r.Code != 0 {
t.Fatalf("%d OK or 0 expected, received %d\n", http.StatusOK, r.Code)
}
newAuthConfig := registry.NewRegistry(runtime.root).GetAuthConfig(false)
if newAuthConfig.Username != authConfig.Username ||
newAuthConfig.Email != authConfig.Email {
t.Fatalf("The auth configuration hasn't been set correctly")
}
}
func TestGetVersion(t *testing.T) {
@@ -73,11 +90,11 @@ func TestGetVersion(t *testing.T) {
r := httptest.NewRecorder()
if err := getVersion(srv, API_VERSION, r, nil, nil); err != nil {
if err := getVersion(srv, APIVERSION, r, nil, nil); err != nil {
t.Fatal(err)
}
v := &ApiVersion{}
v := &APIVersion{}
if err = json.Unmarshal(r.Body.Bytes(), v); err != nil {
t.Fatal(err)
}
@@ -97,21 +114,21 @@ func TestGetInfo(t *testing.T) {
r := httptest.NewRecorder()
if err := getInfo(srv, API_VERSION, r, nil, nil); err != nil {
if err := getInfo(srv, APIVERSION, r, nil, nil); err != nil {
t.Fatal(err)
}
infos := &ApiInfo{}
infos := &APIInfo{}
err = json.Unmarshal(r.Body.Bytes(), infos)
if err != nil {
t.Fatal(err)
}
if infos.Version != VERSION {
t.Errorf("Excepted version %s, %s found", VERSION, infos.Version)
if infos.Images != 1 {
t.Errorf("Excepted images: %d, %d found", 1, infos.Images)
}
}
func TestGetImagesJson(t *testing.T) {
func TestGetImagesJSON(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
@@ -128,11 +145,11 @@ func TestGetImagesJson(t *testing.T) {
r := httptest.NewRecorder()
if err := getImagesJson(srv, API_VERSION, r, req, nil); err != nil {
if err := getImagesJSON(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err)
}
images := []ApiImages{}
images := []APIImages{}
if err := json.Unmarshal(r.Body.Bytes(), &images); err != nil {
t.Fatal(err)
}
@@ -153,11 +170,11 @@ func TestGetImagesJson(t *testing.T) {
t.Fatal(err)
}
if err := getImagesJson(srv, API_VERSION, r2, req2, nil); err != nil {
if err := getImagesJSON(srv, APIVERSION, r2, req2, nil); err != nil {
t.Fatal(err)
}
images2 := []ApiImages{}
images2 := []APIImages{}
if err := json.Unmarshal(r2.Body.Bytes(), &images2); err != nil {
t.Fatal(err)
}
@@ -166,8 +183,8 @@ func TestGetImagesJson(t *testing.T) {
t.Errorf("Excepted 1 image, %d found", len(images2))
}
if images2[0].Id != GetTestImage(runtime).Id {
t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).Id, images2[0].Id)
if images2[0].ID != GetTestImage(runtime).ID {
t.Errorf("Retrieved image Id differs, expected %s, received %s", GetTestImage(runtime).ID, images2[0].ID)
}
r3 := httptest.NewRecorder()
@@ -178,11 +195,11 @@ func TestGetImagesJson(t *testing.T) {
t.Fatal(err)
}
if err := getImagesJson(srv, API_VERSION, r3, req3, nil); err != nil {
if err := getImagesJSON(srv, APIVERSION, r3, req3, nil); err != nil {
t.Fatal(err)
}
images3 := []ApiImages{}
images3 := []APIImages{}
if err := json.Unmarshal(r3.Body.Bytes(), &images3); err != nil {
t.Fatal(err)
}
@@ -199,7 +216,7 @@ func TestGetImagesJson(t *testing.T) {
t.Fatal(err)
}
err = getImagesJson(srv, API_VERSION, r4, req4, nil)
err = getImagesJSON(srv, APIVERSION, r4, req4, nil)
if err == nil {
t.Fatalf("Error expected, received none")
}
@@ -220,7 +237,7 @@ func TestGetImagesViz(t *testing.T) {
srv := &Server{runtime: runtime}
r := httptest.NewRecorder()
if err := getImagesViz(srv, API_VERSION, r, nil, nil); err != nil {
if err := getImagesViz(srv, APIVERSION, r, nil, nil); err != nil {
t.Fatal(err)
}
@@ -256,11 +273,11 @@ func TestGetImagesSearch(t *testing.T) {
t.Fatal(err)
}
if err := getImagesSearch(srv, API_VERSION, r, req, nil); err != nil {
if err := getImagesSearch(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err)
}
results := []ApiSearch{}
results := []APISearch{}
if err := json.Unmarshal(r.Body.Bytes(), &results); err != nil {
t.Fatal(err)
}
@@ -280,11 +297,11 @@ func TestGetImagesHistory(t *testing.T) {
r := httptest.NewRecorder()
if err := getImagesHistory(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
if err := getImagesHistory(srv, APIVERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
t.Fatal(err)
}
history := []ApiHistory{}
history := []APIHistory{}
if err := json.Unmarshal(r.Body.Bytes(), &history); err != nil {
t.Fatal(err)
}
@@ -303,7 +320,7 @@ func TestGetImagesByName(t *testing.T) {
srv := &Server{runtime: runtime}
r := httptest.NewRecorder()
if err := getImagesByName(srv, API_VERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
if err := getImagesByName(srv, APIVERSION, r, nil, map[string]string{"name": unitTestImageName}); err != nil {
t.Fatal(err)
}
@@ -311,12 +328,12 @@ func TestGetImagesByName(t *testing.T) {
if err := json.Unmarshal(r.Body.Bytes(), img); err != nil {
t.Fatal(err)
}
if img.Id != GetTestImage(runtime).Id || img.Comment != "Imported from http://get.docker.io/images/busybox" {
if img.ID != GetTestImage(runtime).ID || img.Comment != "Imported from http://get.docker.io/images/busybox" {
t.Errorf("Error inspecting image")
}
}
func TestGetContainersJson(t *testing.T) {
func TestGetContainersJSON(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
@@ -326,7 +343,7 @@ func TestGetContainersJson(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"},
})
if err != nil {
@@ -340,18 +357,18 @@ func TestGetContainersJson(t *testing.T) {
}
r := httptest.NewRecorder()
if err := getContainersJson(srv, API_VERSION, r, req, nil); err != nil {
if err := getContainersJSON(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err)
}
containers := []ApiContainers{}
containers := []APIContainers{}
if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
t.Fatal(err)
}
if len(containers) != 1 {
t.Fatalf("Excepted %d container, %d found", 1, len(containers))
}
if containers[0].Id != container.Id {
t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.Id, containers[0].Id)
if containers[0].ID != container.ID {
t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", container.ID, containers[0].ID)
}
}
@@ -369,7 +386,7 @@ func TestGetContainersExport(t *testing.T) {
// Create a container and remove a file
container, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
},
)
@@ -383,7 +400,7 @@ func TestGetContainersExport(t *testing.T) {
}
r := httptest.NewRecorder()
if err = getContainersExport(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
if err = getContainersExport(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err)
}
@@ -424,7 +441,7 @@ func TestGetContainersChanges(t *testing.T) {
// Create a container and remove a file
container, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/rm", "/etc/passwd"},
},
)
@@ -438,7 +455,7 @@ func TestGetContainersChanges(t *testing.T) {
}
r := httptest.NewRecorder()
if err := getContainersChanges(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
if err := getContainersChanges(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err)
}
changes := []Change{}
@@ -472,7 +489,7 @@ func TestGetContainersByName(t *testing.T) {
// Create a container and remove a file
container, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"},
},
)
@@ -482,49 +499,15 @@ func TestGetContainersByName(t *testing.T) {
defer runtime.Destroy(container)
r := httptest.NewRecorder()
if err := getContainersByName(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
if err := getContainersByName(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err)
}
outContainer := &Container{}
if err := json.Unmarshal(r.Body.Bytes(), outContainer); err != nil {
t.Fatal(err)
}
if outContainer.Id != container.Id {
t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.Id, outContainer.Id)
}
}
func TestPostAuth(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{
runtime: runtime,
}
config := &auth.AuthConfig{
Username: "utest",
Email: "utest@yopmail.com",
}
authStr := auth.EncodeAuth(config)
auth.SaveConfig(runtime.root, authStr, config.Email)
r := httptest.NewRecorder()
if err := getAuth(srv, API_VERSION, r, nil, nil); err != nil {
t.Fatal(err)
}
authConfig := &auth.AuthConfig{}
if err := json.Unmarshal(r.Body.Bytes(), authConfig); err != nil {
t.Fatal(err)
}
if authConfig.Username != config.Username || authConfig.Email != config.Email {
t.Errorf("The retrieve auth mismatch with the one set.")
if outContainer.ID != container.ID {
t.Fatalf("Wrong containers retrieved. Expected %s, recieved %s", container.ID, outContainer.ID)
}
}
@@ -542,7 +525,7 @@ func TestPostCommit(t *testing.T) {
// Create a container and remove a file
container, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
},
)
@@ -555,24 +538,24 @@ func TestPostCommit(t *testing.T) {
t.Fatal(err)
}
req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+container.Id, bytes.NewReader([]byte{}))
req, err := http.NewRequest("POST", "/commit?repo=testrepo&testtag=tag&container="+container.ID, bytes.NewReader([]byte{}))
if err != nil {
t.Fatal(err)
}
r := httptest.NewRecorder()
if err := postCommit(srv, API_VERSION, r, req, nil); err != nil {
if err := postCommit(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err)
}
if r.Code != http.StatusCreated {
t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
}
apiId := &ApiId{}
if err := json.Unmarshal(r.Body.Bytes(), apiId); err != nil {
apiID := &APIID{}
if err := json.Unmarshal(r.Body.Bytes(), apiID); err != nil {
t.Fatal(err)
}
if _, err := runtime.graph.Get(apiId.Id); err != nil {
if _, err := runtime.graph.Get(apiID.ID); err != nil {
t.Fatalf("The image has not been commited")
}
}
@@ -715,7 +698,7 @@ func TestPostImagesInsert(t *testing.T) {
// t.Fatalf("The test file has not been found")
// }
// if err := srv.runtime.graph.Delete(img.Id); err != nil {
// if err := srv.runtime.graph.Delete(img.ID); err != nil {
// t.Fatal(err)
// }
}
@@ -824,8 +807,8 @@ func TestPostContainersCreate(t *testing.T) {
srv := &Server{runtime: runtime}
configJson, err := json.Marshal(&Config{
Image: GetTestImage(runtime).Id,
configJSON, err := json.Marshal(&Config{
Image: GetTestImage(runtime).ID,
Memory: 33554432,
Cmd: []string{"touch", "/test"},
})
@@ -833,25 +816,25 @@ func TestPostContainersCreate(t *testing.T) {
t.Fatal(err)
}
req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJson))
req, err := http.NewRequest("POST", "/containers/create", bytes.NewReader(configJSON))
if err != nil {
t.Fatal(err)
}
r := httptest.NewRecorder()
if err := postContainersCreate(srv, API_VERSION, r, req, nil); err != nil {
if err := postContainersCreate(srv, APIVERSION, r, req, nil); err != nil {
t.Fatal(err)
}
if r.Code != http.StatusCreated {
t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code)
}
apiRun := &ApiRun{}
apiRun := &APIRun{}
if err := json.Unmarshal(r.Body.Bytes(), apiRun); err != nil {
t.Fatal(err)
}
container := srv.runtime.Get(apiRun.Id)
container := srv.runtime.Get(apiRun.ID)
if container == nil {
t.Fatalf("Container not created")
}
@@ -880,7 +863,7 @@ func TestPostContainersKill(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@@ -902,7 +885,7 @@ func TestPostContainersKill(t *testing.T) {
}
r := httptest.NewRecorder()
if err := postContainersKill(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
if err := postContainersKill(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err)
}
if r.Code != http.StatusNoContent {
@@ -924,7 +907,7 @@ func TestPostContainersRestart(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@@ -945,12 +928,12 @@ func TestPostContainersRestart(t *testing.T) {
t.Errorf("Container should be running")
}
req, err := http.NewRequest("POST", "/containers/"+container.Id+"/restart?t=1", bytes.NewReader([]byte{}))
req, err := http.NewRequest("POST", "/containers/"+container.ID+"/restart?t=1", bytes.NewReader([]byte{}))
if err != nil {
t.Fatal(err)
}
r := httptest.NewRecorder()
if err := postContainersRestart(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
if err := postContainersRestart(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err)
}
if r.Code != http.StatusNoContent {
@@ -980,7 +963,7 @@ func TestPostContainersStart(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@@ -991,7 +974,7 @@ func TestPostContainersStart(t *testing.T) {
defer runtime.Destroy(container)
r := httptest.NewRecorder()
if err := postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
if err := postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err)
}
if r.Code != http.StatusNoContent {
@@ -1006,7 +989,7 @@ func TestPostContainersStart(t *testing.T) {
}
r = httptest.NewRecorder()
if err = postContainersStart(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err == nil {
if err = postContainersStart(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err == nil {
t.Fatalf("A running containter should be able to be started")
}
@@ -1026,7 +1009,7 @@ func TestPostContainersStop(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@@ -1048,12 +1031,12 @@ func TestPostContainersStop(t *testing.T) {
}
// Note: as it is a POST request, it requires a body.
req, err := http.NewRequest("POST", "/containers/"+container.Id+"/stop?t=1", bytes.NewReader([]byte{}))
req, err := http.NewRequest("POST", "/containers/"+container.ID+"/stop?t=1", bytes.NewReader([]byte{}))
if err != nil {
t.Fatal(err)
}
r := httptest.NewRecorder()
if err := postContainersStop(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
if err := postContainersStop(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err)
}
if r.Code != http.StatusNoContent {
@@ -1075,7 +1058,7 @@ func TestPostContainersWait(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sleep", "1"},
OpenStdin: true,
},
@@ -1091,10 +1074,10 @@ func TestPostContainersWait(t *testing.T) {
setTimeout(t, "Wait timed out", 3*time.Second, func() {
r := httptest.NewRecorder()
if err := postContainersWait(srv, API_VERSION, r, nil, map[string]string{"name": container.Id}); err != nil {
if err := postContainersWait(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err)
}
apiWait := &ApiWait{}
apiWait := &APIWait{}
if err := json.Unmarshal(r.Body.Bytes(), apiWait); err != nil {
t.Fatal(err)
}
@@ -1119,7 +1102,7 @@ func TestPostContainersAttach(t *testing.T) {
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
@@ -1148,12 +1131,12 @@ func TestPostContainersAttach(t *testing.T) {
out: stdoutPipe,
}
req, err := http.NewRequest("POST", "/containers/"+container.Id+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
req, err := http.NewRequest("POST", "/containers/"+container.ID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
if err != nil {
t.Fatal(err)
}
if err := postContainersAttach(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
if err := postContainersAttach(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err)
}
}()
@@ -1206,7 +1189,7 @@ func TestDeleteContainers(t *testing.T) {
srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
})
if err != nil {
@@ -1218,19 +1201,19 @@ func TestDeleteContainers(t *testing.T) {
t.Fatal(err)
}
req, err := http.NewRequest("DELETE", "/containers/"+container.Id, nil)
req, err := http.NewRequest("DELETE", "/containers/"+container.ID, nil)
if err != nil {
t.Fatal(err)
}
r := httptest.NewRecorder()
if err := deleteContainers(srv, API_VERSION, r, req, map[string]string{"name": container.Id}); err != nil {
if err := deleteContainers(srv, APIVERSION, r, req, map[string]string{"name": container.ID}); err != nil {
t.Fatal(err)
}
if r.Code != http.StatusNoContent {
t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
}
if c := runtime.Get(container.Id); c != nil {
if c := runtime.Get(container.ID); c != nil {
t.Fatalf("The container as not been deleted")
}
@@ -1239,9 +1222,131 @@ func TestDeleteContainers(t *testing.T) {
}
}
func TestOptionsRoute(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime, enableCors: true}
r := httptest.NewRecorder()
router, err := createRouter(srv, false)
if err != nil {
t.Fatal(err)
}
req, err := http.NewRequest("OPTIONS", "/", nil)
if err != nil {
t.Fatal(err)
}
router.ServeHTTP(r, req)
if r.Code != http.StatusOK {
t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code)
}
}
func TestGetEnabledCors(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime, enableCors: true}
r := httptest.NewRecorder()
router, err := createRouter(srv, false)
if err != nil {
t.Fatal(err)
}
req, err := http.NewRequest("GET", "/version", nil)
if err != nil {
t.Fatal(err)
}
router.ServeHTTP(r, req)
if r.Code != http.StatusOK {
t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code)
}
allowOrigin := r.Header().Get("Access-Control-Allow-Origin")
allowHeaders := r.Header().Get("Access-Control-Allow-Headers")
allowMethods := r.Header().Get("Access-Control-Allow-Methods")
if allowOrigin != "*" {
t.Errorf("Expected header Access-Control-Allow-Origin to be \"*\", %s found.", allowOrigin)
}
if allowHeaders != "Origin, X-Requested-With, Content-Type, Accept" {
t.Errorf("Expected header Access-Control-Allow-Headers to be \"Origin, X-Requested-With, Content-Type, Accept\", %s found.", allowHeaders)
}
if allowMethods != "GET, POST, DELETE, PUT, OPTIONS" {
t.Errorf("Expected hearder Access-Control-Allow-Methods to be \"GET, POST, DELETE, PUT, OPTIONS\", %s found.", allowMethods)
}
}
func TestDeleteImages(t *testing.T) {
//FIXME: Implement this test
t.Log("Test not implemented")
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime}
if err := srv.runtime.repositories.Set("test", "test", unitTestImageName, true); err != nil {
t.Fatal(err)
}
images, err := srv.Images(false, "")
if err != nil {
t.Fatal(err)
}
if len(images) != 2 {
t.Errorf("Excepted 2 images, %d found", len(images))
}
req, err := http.NewRequest("DELETE", "/images/test:test", nil)
if err != nil {
t.Fatal(err)
}
r := httptest.NewRecorder()
if err := deleteImages(srv, APIVERSION, r, req, map[string]string{"name": "test:test"}); err != nil {
t.Fatal(err)
}
if r.Code != http.StatusOK {
t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
}
var outs []APIRmi
if err := json.Unmarshal(r.Body.Bytes(), &outs); err != nil {
t.Fatal(err)
}
if len(outs) != 1 {
t.Fatalf("Expected %d event (untagged), got %d", 1, len(outs))
}
images, err = srv.Images(false, "")
if err != nil {
t.Fatal(err)
}
if len(images) != 1 {
t.Errorf("Excepted 1 image, %d found", len(images))
}
/* if c := runtime.Get(container.Id); c != nil {
t.Fatalf("The container as not been deleted")
}
if _, err := os.Stat(path.Join(container.rwPath(), "test")); err == nil {
t.Fatalf("The test file has not been deleted")
} */
}
// Mocked types for tests

View File

@@ -1,12 +1,18 @@
package docker
import (
"archive/tar"
"bufio"
"bytes"
"errors"
"fmt"
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
)
type Archive io.Reader
@@ -20,6 +26,37 @@ const (
Xz
)
func DetectCompression(source []byte) Compression {
for _, c := range source[:10] {
utils.Debugf("%x", c)
}
sourceLen := len(source)
for compression, m := range map[Compression][]byte{
Bzip2: {0x42, 0x5A, 0x68},
Gzip: {0x1F, 0x8B, 0x08},
Xz: {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00},
} {
fail := false
if len(m) > sourceLen {
utils.Debugf("Len too short")
continue
}
i := 0
for _, b := range m {
if b != source[i] {
fail = true
break
}
i++
}
if !fail {
return compression
}
}
return Uncompressed
}
func (compression *Compression) Flag() string {
switch *compression {
case Bzip2:
@@ -46,14 +83,46 @@ func (compression *Compression) Extension() string {
return ""
}
// Tar creates an archive from the directory at `path`, and returns it as a
// stream of bytes.
func Tar(path string, compression Compression) (io.Reader, error) {
cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".")
return CmdStream(cmd)
return TarFilter(path, compression, nil)
}
// Tar creates an archive from the directory at `path`, only including files whose relative
// paths are included in `filter`. If `filter` is nil, then all files are included.
func TarFilter(path string, compression Compression, filter []string) (io.Reader, error) {
args := []string{"tar", "-f", "-", "-C", path}
if filter == nil {
filter = []string{"."}
}
for _, f := range filter {
args = append(args, "-c"+compression.Flag(), f)
}
return CmdStream(exec.Command(args[0], args[1:]...))
}
// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
// and unpacks it into the directory at `path`.
// The archive may be compressed with one of the following algorithgms:
// identity (uncompressed), gzip, bzip2, xz.
// FIXME: specify behavior when target path exists vs. doesn't exist.
func Untar(archive io.Reader, path string) error {
cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
cmd.Stdin = archive
bufferedArchive := bufio.NewReaderSize(archive, 10)
buf, err := bufferedArchive.Peek(10)
if err != nil {
return err
}
compression := DetectCompression(buf)
utils.Debugf("Archive compression detected: %s", compression.Extension())
cmd := exec.Command("tar", "-f", "-", "-C", path, "-x"+compression.Flag())
cmd.Stdin = bufferedArchive
// Hardcode locale environment for predictable outcome regardless of host configuration.
// (see https://github.com/dotcloud/docker/issues/355)
cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("%s: %s", err, output)
@@ -61,6 +130,95 @@ func Untar(archive io.Reader, path string) error {
return nil
}
// TarUntar is a convenience function which calls Tar and Untar, with
// the output of one piped into the other. If either Tar or Untar fails,
// TarUntar aborts and returns the error.
func TarUntar(src string, filter []string, dst string) error {
utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
archive, err := TarFilter(src, Uncompressed, filter)
if err != nil {
return err
}
return Untar(archive, dst)
}
// UntarPath is a convenience function which looks for an archive
// at filesystem path `src`, and unpacks it at `dst`.
func UntarPath(src, dst string) error {
if archive, err := os.Open(src); err != nil {
return err
} else if err := Untar(archive, dst); err != nil {
return err
}
return nil
}
// CopyWithTar creates a tar archive of filesystem path `src`, and
// unpacks it at filesystem path `dst`.
// The archive is streamed directly with fixed buffering and no
// intermediary disk IO.
//
func CopyWithTar(src, dst string) error {
srcSt, err := os.Stat(src)
if err != nil {
return err
}
if !srcSt.IsDir() {
return CopyFileWithTar(src, dst)
}
// Create dst, copy src's content into it
utils.Debugf("Creating dest directory: %s", dst)
if err := os.MkdirAll(dst, 0700); err != nil && !os.IsExist(err) {
return err
}
utils.Debugf("Calling TarUntar(%s, %s)", src, dst)
return TarUntar(src, nil, dst)
}
// CopyFileWithTar emulates the behavior of the 'cp' command-line
// for a single file. It copies a regular file from path `src` to
// path `dst`, and preserves all its metadata.
//
// If `dst` ends with a trailing slash '/', the final destination path
// will be `dst/base(src)`.
func CopyFileWithTar(src, dst string) error {
utils.Debugf("CopyFileWithTar(%s, %s)", src, dst)
srcSt, err := os.Stat(src)
if err != nil {
return err
}
if srcSt.IsDir() {
return fmt.Errorf("Can't copy a directory")
}
// Clean up the trailing /
if dst[len(dst)-1] == '/' {
dst = path.Join(dst, filepath.Base(src))
}
// Create the holding directory if necessary
if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
return err
}
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
hdr, err := tar.FileInfoHeader(srcSt, "")
if err != nil {
return err
}
hdr.Name = filepath.Base(dst)
if err := tw.WriteHeader(hdr); err != nil {
return err
}
srcF, err := os.Open(src)
if err != nil {
return err
}
if _, err := io.Copy(tw, srcF); err != nil {
return err
}
tw.Close()
return Untar(buf, filepath.Dir(dst))
}
// CmdStream executes a command, and returns its stdout as a stream.
// If the command fails to run or doesn't complete successfully, an error
// will be returned, including anything written on stderr.

View File

@@ -1,10 +1,13 @@
package docker
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path"
"testing"
"time"
)
@@ -58,20 +61,58 @@ func TestCmdStreamGood(t *testing.T) {
}
}
func TestTarUntar(t *testing.T) {
archive, err := Tar(".", Uncompressed)
func tarUntar(t *testing.T, origin string, compression Compression) error {
archive, err := Tar(origin, compression)
if err != nil {
t.Fatal(err)
}
buf := make([]byte, 10)
if _, err := archive.Read(buf); err != nil {
return err
}
archive = io.MultiReader(bytes.NewReader(buf), archive)
detectedCompression := DetectCompression(buf)
if detectedCompression.Extension() != compression.Extension() {
return fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
}
tmp, err := ioutil.TempDir("", "docker-test-untar")
if err != nil {
t.Fatal(err)
return err
}
defer os.RemoveAll(tmp)
if err := Untar(archive, tmp); err != nil {
t.Fatal(err)
return err
}
if _, err := os.Stat(tmp); err != nil {
t.Fatalf("Error stating %s: %s", tmp, err.Error())
return err
}
return nil
}
func TestTarUntar(t *testing.T) {
origin, err := ioutil.TempDir("", "docker-test-untar-origin")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(origin)
if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
t.Fatal(err)
}
for _, c := range []Compression{
Uncompressed,
Gzip,
Bzip2,
Xz,
} {
if err := tarUntar(t, origin, c); err != nil {
t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
}
}
}

View File

@@ -16,12 +16,12 @@ import (
const CONFIGFILE = ".dockercfg"
// the registry server we want to login against
const INDEX_SERVER = "https://index.docker.io/v1"
const INDEXSERVER = "https://index.docker.io/v1"
//const INDEX_SERVER = "http://indexstaging-docker.dotcloud.com/"
//const INDEXSERVER = "http://indexstaging-docker.dotcloud.com/"
var (
ErrConfigFileMissing error = errors.New("The Auth config file is missing")
ErrConfigFileMissing = errors.New("The Auth config file is missing")
)
type AuthConfig struct {
@@ -44,11 +44,11 @@ func IndexServerAddress() string {
if os.Getenv("DOCKER_INDEX_URL") != "" {
return os.Getenv("DOCKER_INDEX_URL") + "/v1"
}
return INDEX_SERVER
return INDEXSERVER
}
// create a base64 encoded auth string to store in config
func EncodeAuth(authConfig *AuthConfig) string {
func encodeAuth(authConfig *AuthConfig) string {
authStr := authConfig.Username + ":" + authConfig.Password
msg := []byte(authStr)
encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
@@ -57,7 +57,7 @@ func EncodeAuth(authConfig *AuthConfig) string {
}
// decode the auth string
func DecodeAuth(authStr string) (*AuthConfig, error) {
func decodeAuth(authStr string) (*AuthConfig, error) {
decLen := base64.StdEncoding.DecodedLen(len(authStr))
decoded := make([]byte, decLen)
authByte := []byte(authStr)
@@ -82,7 +82,7 @@ func DecodeAuth(authStr string) (*AuthConfig, error) {
func LoadConfig(rootPath string) (*AuthConfig, error) {
confFile := path.Join(rootPath, CONFIGFILE)
if _, err := os.Stat(confFile); err != nil {
return nil, ErrConfigFileMissing
return &AuthConfig{rootPath: rootPath}, ErrConfigFileMissing
}
b, err := ioutil.ReadFile(confFile)
if err != nil {
@@ -94,7 +94,7 @@ func LoadConfig(rootPath string) (*AuthConfig, error) {
}
origAuth := strings.Split(arr[0], " = ")
origEmail := strings.Split(arr[1], " = ")
authConfig, err := DecodeAuth(origAuth[1])
authConfig, err := decodeAuth(origAuth[1])
if err != nil {
return nil, err
}
@@ -104,13 +104,13 @@ 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 {
func SaveConfig(authConfig *AuthConfig) error {
confFile := path.Join(authConfig.rootPath, CONFIGFILE)
if len(authConfig.Email) == 0 {
os.Remove(confFile)
return nil
}
lines := "auth = " + authStr + "\n" + "email = " + email + "\n"
lines := "auth = " + encodeAuth(authConfig) + "\n" + "email = " + authConfig.Email + "\n"
b := []byte(lines)
err := ioutil.WriteFile(confFile, b, 0600)
if err != nil {
@@ -120,7 +120,7 @@ func SaveConfig(rootPath, authStr string, email string) error {
}
// try to register/login to the registry server
func Login(authConfig *AuthConfig) (string, error) {
func Login(authConfig *AuthConfig, store bool) (string, error) {
storeConfig := false
client := &http.Client{}
reqStatusCode := 0
@@ -146,7 +146,7 @@ func Login(authConfig *AuthConfig) (string, error) {
if reqStatusCode == 201 {
status = "Account created. Please use the confirmation link we sent" +
" to your e-mail to activate it.\n"
" to your e-mail to activate it."
storeConfig = true
} else if reqStatusCode == 403 {
return "", fmt.Errorf("Login: Your account hasn't been activated. " +
@@ -165,11 +165,14 @@ func Login(authConfig *AuthConfig) (string, error) {
return "", err
}
if resp.StatusCode == 200 {
status = "Login Succeeded\n"
status = "Login Succeeded"
storeConfig = true
} else if resp.StatusCode == 401 {
if err := SaveConfig(authConfig.rootPath, "", ""); err != nil {
return "", err
if store {
authConfig.Email = ""
if err := SaveConfig(authConfig); err != nil {
return "", err
}
}
return "", fmt.Errorf("Wrong login/password, please try again")
} else {
@@ -182,9 +185,8 @@ func Login(authConfig *AuthConfig) (string, error) {
} else {
return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
}
if storeConfig {
authStr := EncodeAuth(authConfig)
if err := SaveConfig(authConfig.rootPath, authStr, authConfig.Email); err != nil {
if storeConfig && store {
if err := SaveConfig(authConfig); err != nil {
return "", err
}
}

View File

@@ -10,8 +10,8 @@ import (
func TestEncodeAuth(t *testing.T) {
newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
authStr := EncodeAuth(newAuthConfig)
decAuthConfig, err := DecodeAuth(authStr)
authStr := encodeAuth(newAuthConfig)
decAuthConfig, err := decodeAuth(authStr)
if err != nil {
t.Fatal(err)
}
@@ -30,11 +30,11 @@ func TestLogin(t *testing.T) {
os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com")
defer os.Setenv("DOCKER_INDEX_URL", "")
authConfig := NewAuthConfig("unittester", "surlautrerivejetattendrai", "noise+unittester@dotcloud.com", "/tmp")
status, err := Login(authConfig)
status, err := Login(authConfig, false)
if err != nil {
t.Fatal(err)
}
if status != "Login Succeeded\n" {
if status != "Login Succeeded" {
t.Fatalf("Expected status \"Login Succeeded\", found \"%s\" instead", status)
}
}
@@ -50,17 +50,17 @@ func TestCreateAccount(t *testing.T) {
token := hex.EncodeToString(tokenBuffer)[:12]
username := "ut" + token
authConfig := NewAuthConfig(username, "test42", "docker-ut+"+token+"@example.com", "/tmp")
status, err := Login(authConfig)
status, err := Login(authConfig, false)
if err != nil {
t.Fatal(err)
}
expectedStatus := "Account created. Please use the confirmation link we sent" +
" to your e-mail to activate it.\n"
" to your e-mail to activate it."
if status != expectedStatus {
t.Fatalf("Expected status: \"%s\", found \"%s\" instead.", expectedStatus, status)
}
status, err = Login(authConfig)
status, err = Login(authConfig, false)
if err == nil {
t.Fatalf("Expected error but found nil instead")
}

View File

@@ -2,11 +2,14 @@ package docker
import (
"fmt"
"github.com/dotcloud/docker/utils"
"os"
"path"
"time"
)
var defaultDns = []string{"8.8.8.8", "8.8.4.4"}
type Builder struct {
runtime *Runtime
repositories *TagStore
@@ -40,7 +43,7 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
}
// Generate id
id := GenerateId()
id := GenerateID()
// Generate default hostname
// FIXME: the lxc template no longer needs to set a default hostname
if config.Hostname == "" {
@@ -49,32 +52,43 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
container := &Container{
// FIXME: we should generate the ID here instead of receiving it as an argument
Id: id,
ID: id,
Created: time.Now(),
Path: config.Cmd[0],
Args: config.Cmd[1:], //FIXME: de-duplicate from config
Config: config,
Image: img.Id, // Always use the resolved image id
Image: img.ID, // Always use the resolved image id
NetworkSettings: &NetworkSettings{},
// FIXME: do we need to store this in the container?
SysInitPath: sysInitPath,
}
container.root = builder.runtime.containerRoot(container.Id)
container.root = builder.runtime.containerRoot(container.ID)
// Step 1: create the container directory.
// This doubles as a barrier to avoid race conditions.
if err := os.Mkdir(container.root, 0700); err != nil {
return nil, err
}
if len(config.Dns) == 0 && len(builder.runtime.Dns) == 0 && utils.CheckLocalDns() {
//"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns
builder.runtime.Dns = defaultDns
}
// If custom dns exists, then create a resolv.conf for the container
if len(config.Dns) > 0 {
if len(config.Dns) > 0 || len(builder.runtime.Dns) > 0 {
var dns []string
if len(config.Dns) > 0 {
dns = config.Dns
} else {
dns = builder.runtime.Dns
}
container.ResolvConfPath = path.Join(container.root, "resolv.conf")
f, err := os.Create(container.ResolvConfPath)
if err != nil {
return nil, err
}
defer f.Close()
for _, dns := range config.Dns {
for _, dns := range dns {
if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
return nil, err
}
@@ -110,7 +124,7 @@ func (builder *Builder) Commit(container *Container, repository, tag, comment, a
}
// Register the image if needed
if repository != "" {
if err := builder.repositories.Set(repository, tag, img.Id, true); err != nil {
if err := builder.repositories.Set(repository, tag, img.ID, true); err != nil {
return img, err
}
}

View File

@@ -1,314 +0,0 @@
package docker
import (
"bufio"
"encoding/json"
"fmt"
"github.com/dotcloud/docker/utils"
"io"
"net/url"
"os"
"reflect"
"strings"
)
type builderClient struct {
cli *DockerCli
image string
maintainer string
config *Config
tmpContainers map[string]struct{}
tmpImages map[string]struct{}
needCommit bool
}
func (b *builderClient) clearTmp(containers, images map[string]struct{}) {
for i := range images {
if _, _, err := b.cli.call("DELETE", "/images/"+i, nil); err != nil {
utils.Debugf("%s", err)
}
utils.Debugf("Removing image %s", i)
}
}
func (b *builderClient) CmdFrom(name string) error {
obj, statusCode, err := b.cli.call("GET", "/images/"+name+"/json", nil)
if statusCode == 404 {
remote := name
var tag string
if strings.Contains(remote, ":") {
remoteParts := strings.Split(remote, ":")
tag = remoteParts[1]
remote = remoteParts[0]
}
var out io.Writer
if os.Getenv("DEBUG") != "" {
out = os.Stdout
} else {
out = &utils.NopWriter{}
}
if err := b.cli.stream("POST", "/images/create?fromImage="+remote+"&tag="+tag, nil, out); err != nil {
return err
}
obj, _, err = b.cli.call("GET", "/images/"+name+"/json", nil)
if err != nil {
return err
}
}
if err != nil {
return err
}
img := &ApiId{}
if err := json.Unmarshal(obj, img); err != nil {
return err
}
b.image = img.Id
utils.Debugf("Using image %s", b.image)
return nil
}
func (b *builderClient) CmdMaintainer(name string) error {
b.needCommit = true
b.maintainer = name
return nil
}
func (b *builderClient) CmdRun(args string) error {
if b.image == "" {
return fmt.Errorf("Please provide a source image with `from` prior to run")
}
config, _, err := ParseRun([]string{b.image, "/bin/sh", "-c", args}, nil)
if err != nil {
return err
}
cmd, env := b.config.Cmd, b.config.Env
b.config.Cmd = nil
MergeConfig(b.config, config)
body, statusCode, err := b.cli.call("POST", "/images/getCache", &ApiImageConfig{Id: b.image, Config: b.config})
if err != nil {
if statusCode != 404 {
return err
}
}
if statusCode != 404 {
apiId := &ApiId{}
if err := json.Unmarshal(body, apiId); err != nil {
return err
}
utils.Debugf("Use cached version")
b.image = apiId.Id
return nil
}
cid, err := b.run()
if err != nil {
return err
}
b.config.Cmd, b.config.Env = cmd, env
return b.commit(cid)
}
func (b *builderClient) CmdEnv(args string) error {
b.needCommit = true
tmp := strings.SplitN(args, " ", 2)
if len(tmp) != 2 {
return fmt.Errorf("Invalid ENV format")
}
key := strings.Trim(tmp[0], " ")
value := strings.Trim(tmp[1], " ")
for i, elem := range b.config.Env {
if strings.HasPrefix(elem, key+"=") {
b.config.Env[i] = key + "=" + value
return nil
}
}
b.config.Env = append(b.config.Env, key+"="+value)
return nil
}
func (b *builderClient) CmdCmd(args string) error {
b.needCommit = true
var cmd []string
if err := json.Unmarshal([]byte(args), &cmd); err != nil {
utils.Debugf("Error unmarshalling: %s, using /bin/sh -c", err)
b.config.Cmd = []string{"/bin/sh", "-c", args}
} else {
b.config.Cmd = cmd
}
return nil
}
func (b *builderClient) CmdExpose(args string) error {
ports := strings.Split(args, " ")
b.config.PortSpecs = append(ports, b.config.PortSpecs...)
return nil
}
func (b *builderClient) CmdInsert(args string) error {
// tmp := strings.SplitN(args, "\t ", 2)
// sourceUrl, destPath := tmp[0], tmp[1]
// v := url.Values{}
// v.Set("url", sourceUrl)
// v.Set("path", destPath)
// body, _, err := b.cli.call("POST", "/images/insert?"+v.Encode(), nil)
// if err != nil {
// return err
// }
// apiId := &ApiId{}
// if err := json.Unmarshal(body, apiId); err != nil {
// return err
// }
// FIXME: Reimplement this, we need to retrieve the resulting Id
return fmt.Errorf("INSERT not implemented")
}
func (b *builderClient) run() (string, error) {
if b.image == "" {
return "", fmt.Errorf("Please provide a source image with `from` prior to run")
}
b.config.Image = b.image
body, _, err := b.cli.call("POST", "/containers/create", b.config)
if err != nil {
return "", err
}
apiRun := &ApiRun{}
if err := json.Unmarshal(body, apiRun); err != nil {
return "", err
}
for _, warning := range apiRun.Warnings {
fmt.Fprintln(os.Stderr, "WARNING: ", warning)
}
//start the container
_, _, err = b.cli.call("POST", "/containers/"+apiRun.Id+"/start", nil)
if err != nil {
return "", err
}
b.tmpContainers[apiRun.Id] = struct{}{}
// Wait for it to finish
body, _, err = b.cli.call("POST", "/containers/"+apiRun.Id+"/wait", nil)
if err != nil {
return "", err
}
apiWait := &ApiWait{}
if err := json.Unmarshal(body, apiWait); err != nil {
return "", err
}
if apiWait.StatusCode != 0 {
return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, apiWait.StatusCode)
}
return apiRun.Id, nil
}
func (b *builderClient) commit(id string) error {
if b.image == "" {
return fmt.Errorf("Please provide a source image with `from` prior to run")
}
b.config.Image = b.image
if id == "" {
cmd := b.config.Cmd
b.config.Cmd = []string{"true"}
if cid, err := b.run(); err != nil {
return err
} else {
id = cid
}
b.config.Cmd = cmd
}
// Commit the container
v := url.Values{}
v.Set("container", id)
v.Set("author", b.maintainer)
body, _, err := b.cli.call("POST", "/commit?"+v.Encode(), b.config)
if err != nil {
return err
}
apiId := &ApiId{}
if err := json.Unmarshal(body, apiId); err != nil {
return err
}
b.tmpImages[apiId.Id] = struct{}{}
b.image = apiId.Id
b.needCommit = false
return nil
}
func (b *builderClient) Build(dockerfile, context io.Reader) (string, error) {
defer b.clearTmp(b.tmpContainers, b.tmpImages)
file := bufio.NewReader(dockerfile)
for {
line, err := file.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
return "", err
}
line = strings.Replace(strings.TrimSpace(line), " ", " ", 1)
// Skip comments and empty line
if len(line) == 0 || line[0] == '#' {
continue
}
tmp := strings.SplitN(line, " ", 2)
if len(tmp) != 2 {
return "", fmt.Errorf("Invalid Dockerfile format")
}
instruction := strings.ToLower(strings.Trim(tmp[0], " "))
arguments := strings.Trim(tmp[1], " ")
fmt.Fprintf(os.Stderr, "%s %s (%s)\n", strings.ToUpper(instruction), arguments, b.image)
method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
if !exists {
fmt.Fprintf(os.Stderr, "Skipping unknown instruction %s\n", strings.ToUpper(instruction))
}
ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
if ret != nil {
return "", ret.(error)
}
fmt.Fprintf(os.Stderr, "===> %v\n", b.image)
}
if b.needCommit {
if err := b.commit(""); err != nil {
return "", err
}
}
if b.image != "" {
// The build is successful, keep the temporary containers and images
for i := range b.tmpImages {
delete(b.tmpImages, i)
}
for i := range b.tmpContainers {
delete(b.tmpContainers, i)
}
fmt.Fprintf(os.Stderr, "Build finished. image id: %s\n", b.image)
return b.image, nil
}
return "", fmt.Errorf("An error occured during the build\n")
}
func NewBuilderClient(addr string, port int) BuildFile {
return &builderClient{
cli: NewDockerCli(addr, port),
config: &Config{},
tmpContainers: make(map[string]struct{}),
tmpImages: make(map[string]struct{}),
}
}

View File

@@ -14,7 +14,7 @@ import (
)
type BuildFile interface {
Build(io.Reader, io.Reader) (string, error)
Build(io.Reader) (string, error)
CmdFrom(string) error
CmdRun(string) error
}
@@ -61,7 +61,7 @@ func (b *buildFile) CmdFrom(name string) error {
remote = name
}
if err := b.srv.ImagePull(remote, tag, "", b.out, false); err != nil {
if err := b.srv.ImagePull(remote, tag, "", b.out, utils.NewStreamFormatter(false), nil); err != nil {
return err
}
@@ -73,7 +73,7 @@ func (b *buildFile) CmdFrom(name string) error {
return err
}
}
b.image = image.Id
b.image = image.ID
b.config = &Config{}
return nil
}
@@ -101,8 +101,9 @@ func (b *buildFile) CmdRun(args string) error {
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
return err
} else if cache != nil {
fmt.Fprintf(b.out, " ---> Using cache\n")
utils.Debugf("[BUILDER] Use cached version")
b.image = cache.Id
b.image = cache.ID
return nil
} else {
utils.Debugf("[BUILDER] Cache miss")
@@ -124,8 +125,8 @@ func (b *buildFile) CmdEnv(args string) error {
if len(tmp) != 2 {
return fmt.Errorf("Invalid ENV format")
}
key := strings.Trim(tmp[0], " ")
value := strings.Trim(tmp[1], " ")
key := strings.Trim(tmp[0], " \t")
value := strings.Trim(tmp[1], " \t")
for i, elem := range b.config.Env {
if strings.HasPrefix(elem, key+"=") {
@@ -164,6 +165,45 @@ func (b *buildFile) CmdCopy(args string) error {
return fmt.Errorf("COPY has been deprecated. Please use ADD instead")
}
func (b *buildFile) addRemote(container *Container, orig, dest string) error {
file, err := utils.Download(orig, ioutil.Discard)
if err != nil {
return err
}
defer file.Body.Close()
return container.Inject(file.Body, dest)
}
func (b *buildFile) addContext(container *Container, orig, dest string) error {
origPath := path.Join(b.context, orig)
destPath := path.Join(container.RootfsPath(), dest)
// Preserve the trailing '/'
if dest[len(dest)-1] == '/' {
destPath = destPath + "/"
}
fi, err := os.Stat(origPath)
if err != nil {
return err
}
if fi.IsDir() {
if err := CopyWithTar(origPath, destPath); err != nil {
return err
}
// First try to unpack the source as an archive
} else if err := UntarPath(origPath, destPath); err != nil {
utils.Debugf("Couldn't untar %s to %s: %s", origPath, destPath, err)
// If that fails, just copy it as a regular file
if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil {
return err
}
if err := CopyWithTar(origPath, destPath); err != nil {
return err
}
}
return nil
}
func (b *buildFile) CmdAdd(args string) error {
if b.context == "" {
return fmt.Errorf("No context given. Impossible to use ADD")
@@ -172,55 +212,36 @@ func (b *buildFile) CmdAdd(args string) error {
if len(tmp) != 2 {
return fmt.Errorf("Invalid ADD format")
}
orig := strings.Trim(tmp[0], " ")
dest := strings.Trim(tmp[1], " ")
orig := strings.Trim(tmp[0], " \t")
dest := strings.Trim(tmp[1], " \t")
cmd := b.config.Cmd
b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)}
cid, err := b.run()
b.config.Image = b.image
// Create the container and start it
container, err := b.builder.Create(b.config)
if err != nil {
return err
}
b.tmpContainers[container.ID] = struct{}{}
container := b.runtime.Get(cid)
if container == nil {
return fmt.Errorf("Error while creating the container (CmdAdd)")
}
if err := container.EnsureMounted(); err != nil {
return err
}
defer container.Unmount()
origPath := path.Join(b.context, orig)
destPath := path.Join(container.RootfsPath(), dest)
fi, err := os.Stat(origPath)
if err != nil {
return err
}
if fi.IsDir() {
if err := os.MkdirAll(destPath, 0700); err != nil {
if utils.IsURL(orig) {
if err := b.addRemote(container, orig, dest); err != nil {
return err
}
files, err := ioutil.ReadDir(path.Join(b.context, orig))
if err != nil {
return err
}
for _, fi := range files {
if err := utils.CopyDirectory(path.Join(origPath, fi.Name()), path.Join(destPath, fi.Name())); err != nil {
return err
}
}
} else {
if err := os.MkdirAll(path.Dir(destPath), 0700); err != nil {
return err
}
if err := utils.CopyDirectory(origPath, destPath); err != nil {
if err := b.addContext(container, orig, dest); err != nil {
return err
}
}
if err := b.commit(cid, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil {
if err := b.commit(container.ID, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil {
return err
}
b.config.Cmd = cmd
@@ -238,7 +259,8 @@ func (b *buildFile) run() (string, error) {
if err != nil {
return "", err
}
b.tmpContainers[c.Id] = struct{}{}
b.tmpContainers[c.ID] = struct{}{}
fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(c.ID))
//start the container
if err := c.Start(); err != nil {
@@ -250,7 +272,7 @@ func (b *buildFile) run() (string, error) {
return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret)
}
return c.Id, nil
return c.ID, nil
}
// Commit the container <id> with the autorun command <autoCmd>
@@ -260,23 +282,31 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
}
b.config.Image = b.image
if id == "" {
cmd := b.config.Cmd
b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil {
return err
} else if cache != nil {
fmt.Fprintf(b.out, " ---> Using cache\n")
utils.Debugf("[BUILDER] Use cached version")
b.image = cache.Id
b.image = cache.ID
return nil
} else {
utils.Debugf("[BUILDER] Cache miss")
}
if cid, err := b.run(); err != nil {
container, err := b.builder.Create(b.config)
if err != nil {
return err
} else {
id = cid
}
b.tmpContainers[container.ID] = struct{}{}
fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(container.ID))
id = container.ID
if err := container.EnsureMounted(); err != nil {
return err
}
defer container.Unmount()
}
container := b.runtime.Get(id)
@@ -292,33 +322,40 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
if err != nil {
return err
}
b.tmpImages[image.Id] = struct{}{}
b.image = image.Id
b.tmpImages[image.ID] = struct{}{}
b.image = image.ID
return nil
}
func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
if context != nil {
name, err := ioutil.TempDir("/tmp", "docker-build")
if err != nil {
return "", err
}
if err := Untar(context, name); err != nil {
return "", err
}
defer os.RemoveAll(name)
b.context = name
func (b *buildFile) Build(context io.Reader) (string, error) {
// FIXME: @creack any reason for using /tmp instead of ""?
// FIXME: @creack "name" is a terrible variable name
name, err := ioutil.TempDir("/tmp", "docker-build")
if err != nil {
return "", err
}
if err := Untar(context, name); err != nil {
return "", err
}
defer os.RemoveAll(name)
b.context = name
dockerfile, err := os.Open(path.Join(name, "Dockerfile"))
if err != nil {
return "", fmt.Errorf("Can't build a directory with no Dockerfile")
}
// FIXME: "file" is also a terrible variable name ;)
file := bufio.NewReader(dockerfile)
stepN := 0
for {
line, err := file.ReadString('\n')
if err != nil {
if err == io.EOF {
if err == io.EOF && line == "" {
break
} else if err != io.EOF {
return "", err
}
return "", err
}
line = strings.Replace(strings.TrimSpace(line), " ", " ", 1)
line = strings.Trim(strings.Replace(line, "\t", " ", -1), " \t\r\n")
// Skip comments and empty line
if len(line) == 0 || line[0] == '#' {
continue
@@ -329,12 +366,13 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
}
instruction := strings.ToLower(strings.Trim(tmp[0], " "))
arguments := strings.Trim(tmp[1], " ")
fmt.Fprintf(b.out, "%s %s (%s)\n", strings.ToUpper(instruction), arguments, b.image)
stepN += 1
// FIXME: only count known instructions as build steps
fmt.Fprintf(b.out, "Step %d : %s %s\n", stepN, strings.ToUpper(instruction), arguments)
method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
if !exists {
fmt.Fprintf(b.out, "Skipping unknown instruction %s\n", strings.ToUpper(instruction))
fmt.Fprintf(b.out, "# Skipping unknown instruction %s\n", strings.ToUpper(instruction))
continue
}
ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
@@ -342,10 +380,10 @@ func (b *buildFile) Build(dockerfile, context io.Reader) (string, error) {
return "", ret.(error)
}
fmt.Fprintf(b.out, "===> %v\n", b.image)
fmt.Fprintf(b.out, " ---> %v\n", utils.TruncateID(b.image))
}
if b.image != "" {
fmt.Fprintf(b.out, "Build successful.\n===> %s\n", b.image)
fmt.Fprintf(b.out, "Successfully built %s\n", utils.TruncateID(b.image))
return b.image, nil
}
return "", fmt.Errorf("An error occured during the build\n")

View File

@@ -1,72 +1,102 @@
package docker
import (
"github.com/dotcloud/docker/utils"
"strings"
"io/ioutil"
"testing"
)
const Dockerfile = `
# VERSION 0.1
# DOCKER-VERSION 0.2
// mkTestContext generates a build context from the contents of the provided dockerfile.
// This context is suitable for use as an argument to BuildFile.Build()
func mkTestContext(dockerfile string, files [][2]string, t *testing.T) Archive {
context, err := mkBuildContext(dockerfile, files)
if err != nil {
t.Fatal(err)
}
return context
}
from ` + unitTestImageName + `
// A testContextTemplate describes a build context and how to test it
type testContextTemplate struct {
// Contents of the Dockerfile
dockerfile string
// Additional files in the context, eg [][2]string{"./passwd", "gordon"}
files [][2]string
}
// A table of all the contexts to build and test.
// A new docker runtime will be created and torn down for each context.
var testContexts []testContextTemplate = []testContextTemplate{
{
`
from docker-ut
run sh -c 'echo root:testpass > /tmp/passwd'
run mkdir -p /var/run/sshd
`
run [ "$(cat /tmp/passwd)" = "root:testpass" ]
run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
`,
nil,
},
{
`
from docker-ut
add foo /usr/lib/bla/bar
run [ "$(cat /usr/lib/bla/bar)" = 'hello world!' ]
`,
[][2]string{{"foo", "hello world!"}},
},
{
`
from docker-ut
add f /
run [ "$(cat /f)" = "hello" ]
add f /abc
run [ "$(cat /abc)" = "hello" ]
add f /x/y/z
run [ "$(cat /x/y/z)" = "hello" ]
add f /x/y/d/
run [ "$(cat /x/y/d/f)" = "hello" ]
add d /
run [ "$(cat /ga)" = "bu" ]
add d /somewhere
run [ "$(cat /somewhere/ga)" = "bu" ]
add d /anotherplace/
run [ "$(cat /anotherplace/ga)" = "bu" ]
add d /somewheeeere/over/the/rainbooow
run [ "$(cat /somewheeeere/over/the/rainbooow/ga)" = "bu" ]
`,
[][2]string{
{"f", "hello"},
{"d/ga", "bu"},
},
},
{
`
from docker-ut
env FOO BAR
run [ "$FOO" = "BAR" ]
`,
nil,
},
}
// FIXME: test building with 2 successive overlapping ADD commands
func TestBuild(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
for _, ctx := range testContexts {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime}
srv := &Server{runtime: runtime}
buildfile := NewBuildFile(srv, &utils.NopWriter{})
imgId, err := buildfile.Build(strings.NewReader(Dockerfile), nil)
if err != nil {
t.Fatal(err)
}
builder := NewBuilder(runtime)
container, err := builder.Create(
&Config{
Image: imgId,
Cmd: []string{"cat", "/tmp/passwd"},
},
)
if err != nil {
t.Fatal(err)
}
defer runtime.Destroy(container)
output, err := container.Output()
if err != nil {
t.Fatal(err)
}
if string(output) != "root:testpass\n" {
t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, "root:testpass\n")
}
container2, err := builder.Create(
&Config{
Image: imgId,
Cmd: []string{"ls", "-d", "/var/run/sshd"},
},
)
if err != nil {
t.Fatal(err)
}
defer runtime.Destroy(container2)
output, err = container2.Output()
if err != nil {
t.Fatal(err)
}
if string(output) != "/var/run/sshd\n" {
t.Fatal("/var/run/sshd has not been created")
buildfile := NewBuildFile(srv, ioutil.Discard)
if _, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t)); err != nil {
t.Fatal(err)
}
}
}

View File

@@ -65,7 +65,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
file := filepath.Base(path)
// If there is a whiteout, then the file was removed
if strings.HasPrefix(file, ".wh.") {
originalFile := strings.TrimLeft(file, ".wh.")
originalFile := file[len(".wh."):]
change.Path = filepath.Join(filepath.Dir(path), originalFile)
change.Kind = ChangeDelete
} else {

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,7 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"sort"
"strconv"
"strings"
@@ -23,7 +24,7 @@ import (
type Container struct {
root string
Id string
ID string
Created time.Time
@@ -75,7 +76,7 @@ type Config struct {
}
func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet, error) {
cmd := Subcmd("run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
if len(args) > 0 && args[0] != "--help" {
cmd.SetOutput(ioutil.Discard)
}
@@ -167,8 +168,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *flag.FlagSet
}
type NetworkSettings struct {
IpAddress string
IpPrefixLen int
IPAddress string
IPPrefixLen int
Gateway string
Bridge string
PortMapping map[string]string
@@ -355,6 +356,18 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
errors <- err
}()
}
} else {
go func() {
if stdinCloser != nil {
defer stdinCloser.Close()
}
if cStdout, err := container.StdoutPipe(); err != nil {
utils.Debugf("Error stdout pipe")
} else {
io.Copy(&utils.NopWriter{}, cStdout)
}
}()
}
if stderr != nil {
nJobs += 1
@@ -381,7 +394,20 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
errors <- err
}()
}
} else {
go func() {
if stdinCloser != nil {
defer stdinCloser.Close()
}
if cStderr, err := container.StderrPipe(); err != nil {
utils.Debugf("Error stdout pipe")
} else {
io.Copy(&utils.NopWriter{}, cStderr)
}
}()
}
return utils.Go(func() error {
if cStdout != nil {
defer cStdout.Close()
@@ -409,7 +435,7 @@ func (container *Container) Start() error {
defer container.State.unlock()
if container.State.Running {
return fmt.Errorf("The container %s is already running.", container.Id)
return fmt.Errorf("The container %s is already running.", container.ID)
}
if err := container.EnsureMounted(); err != nil {
return err
@@ -431,24 +457,24 @@ func (container *Container) Start() error {
// Create the requested volumes volumes
for volPath := range container.Config.Volumes {
if c, err := container.runtime.volumes.Create(nil, container, "", "", nil); err != nil {
c, err := container.runtime.volumes.Create(nil, container, "", "", nil)
if 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 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)
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)
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
@@ -462,7 +488,7 @@ func (container *Container) Start() error {
}
params := []string{
"-n", container.Id,
"-n", container.ID,
"-f", container.lxcConfigPath(),
"--",
"/sbin/init",
@@ -573,17 +599,17 @@ func (container *Container) allocateNetwork() error {
}
container.NetworkSettings.PortMapping = make(map[string]string)
for _, spec := range container.Config.PortSpecs {
if nat, err := iface.AllocatePort(spec); err != nil {
nat, err := iface.AllocatePort(spec)
if err != nil {
iface.Release()
return err
} else {
container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend)
}
container.NetworkSettings.PortMapping[strconv.Itoa(nat.Backend)] = strconv.Itoa(nat.Frontend)
}
container.network = iface
container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
container.NetworkSettings.IpAddress = iface.IPNet.IP.String()
container.NetworkSettings.IpPrefixLen, _ = iface.IPNet.Mask.Size()
container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
container.NetworkSettings.Gateway = iface.Gateway.String()
return nil
}
@@ -597,16 +623,15 @@ func (container *Container) releaseNetwork() {
// 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 {
output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
if err != nil {
return err
} else {
if !strings.Contains(string(output), "RUNNING") {
return nil
}
}
if !strings.Contains(string(output), "RUNNING") {
return nil
}
time.Sleep(500 * time.Millisecond)
}
return nil
}
func (container *Container) monitor() {
@@ -616,17 +641,17 @@ func (container *Container) monitor() {
// If the command does not exists, try to wait via lxc
if container.cmd == nil {
if err := container.waitLxc(); err != nil {
utils.Debugf("%s: Process: %s", container.Id, err)
utils.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
utils.Debugf("%s: Process: %s", container.Id, err)
utils.Debugf("%s: Process: %s", container.ID, err)
}
}
utils.Debugf("Process finished")
var exitCode int = -1
exitCode := -1
if container.cmd != nil {
exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
}
@@ -635,24 +660,24 @@ func (container *Container) monitor() {
container.releaseNetwork()
if container.Config.OpenStdin {
if err := container.stdin.Close(); err != nil {
utils.Debugf("%s: Error close stdin: %s", container.Id, err)
utils.Debugf("%s: Error close stdin: %s", container.ID, err)
}
}
if err := container.stdout.CloseWriters(); err != nil {
utils.Debugf("%s: Error close stdout: %s", container.Id, err)
utils.Debugf("%s: Error close stdout: %s", container.ID, err)
}
if err := container.stderr.CloseWriters(); err != nil {
utils.Debugf("%s: Error close stderr: %s", container.Id, err)
utils.Debugf("%s: Error close stderr: %s", container.ID, err)
}
if container.ptyMaster != nil {
if err := container.ptyMaster.Close(); err != nil {
utils.Debugf("%s: Error closing Pty master: %s", container.Id, err)
utils.Debugf("%s: Error closing Pty master: %s", container.ID, err)
}
}
if err := container.Unmount(); err != nil {
log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
}
// Re-create a brand new stdin pipe once the container exited
@@ -673,7 +698,7 @@ func (container *Container) monitor() {
// This is because State.setStopped() has already been called, and has caused Wait()
// to return.
// FIXME: why are we serializing running state to disk in the first place?
//log.Printf("%s: Failed to dump configuration to the disk: %s", container.Id, err)
//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
}
}
@@ -683,17 +708,17 @@ func (container *Container) kill() error {
}
// Sending SIGKILL to the process via lxc
output, err := exec.Command("lxc-kill", "-n", container.Id, "9").CombinedOutput()
output, err := exec.Command("lxc-kill", "-n", container.ID, "9").CombinedOutput()
if err != nil {
log.Printf("error killing container %s (%s, %s)", container.Id, output, err)
log.Printf("error killing container %s (%s, %s)", container.ID, output, err)
}
// 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)
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)
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
}
@@ -721,7 +746,7 @@ func (container *Container) Stop(seconds int) error {
}
// 1. Send a SIGTERM
if output, err := exec.Command("lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
if output, err := exec.Command("lxc-kill", "-n", container.ID, "15").CombinedOutput(); err != nil {
log.Print(string(output))
log.Print("Failed to send SIGTERM to the process, force killing")
if err := container.kill(); err != nil {
@@ -731,7 +756,7 @@ func (container *Container) Stop(seconds int) error {
// 2. Wait for the process to exit on its own
if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.Id, seconds)
log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
if err := container.kill(); err != nil {
return err
}
@@ -795,7 +820,6 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
case <-done:
return nil
}
panic("unreachable")
}
func (container *Container) EnsureMounted() error {
@@ -838,16 +862,16 @@ func (container *Container) Unmount() error {
return Unmount(container.RootfsPath())
}
// ShortId returns a shorthand version of the container's id for convenience.
// ShortID returns a shorthand version of the container's id for convenience.
// A collision with other container shorthands is very unlikely, but possible.
// In case of a collision a lookup with Runtime.Get() will fail, and the caller
// will need to use a langer prefix, or the full-length container Id.
func (container *Container) ShortId() string {
return utils.TruncateId(container.Id)
func (container *Container) ShortID() string {
return utils.TruncateID(container.ID)
}
func (container *Container) logPath(name string) string {
return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name))
return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.ID, name))
}
func (container *Container) ReadLog(name string) (io.Reader, error) {
@@ -887,9 +911,32 @@ func (container *Container) rwPath() string {
return path.Join(container.root, "rw")
}
func validateId(id string) error {
func validateID(id string) error {
if id == "" {
return fmt.Errorf("Invalid empty id")
}
return nil
}
// GetSize, return real size, virtual size
func (container *Container) GetSize() (int64, int64) {
var sizeRw, sizeRootfs int64
filepath.Walk(container.rwPath(), func(path string, fileInfo os.FileInfo, err error) error {
if fileInfo != nil {
sizeRw += fileInfo.Size()
}
return nil
})
_, err := os.Stat(container.RootfsPath())
if err == nil {
filepath.Walk(container.RootfsPath(), func(path string, fileInfo os.FileInfo, err error) error {
if fileInfo != nil {
sizeRootfs += fileInfo.Size()
}
return nil
})
}
return sizeRw, sizeRootfs
}

View File

@@ -14,7 +14,7 @@ import (
"time"
)
func TestIdFormat(t *testing.T) {
func TestIDFormat(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
@@ -22,19 +22,19 @@ func TestIdFormat(t *testing.T) {
defer nuke(runtime)
container1, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
},
)
if err != nil {
t.Fatal(err)
}
match, err := regexp.Match("^[0-9a-f]{64}$", []byte(container1.Id))
match, err := regexp.Match("^[0-9a-f]{64}$", []byte(container1.ID))
if err != nil {
t.Fatal(err)
}
if !match {
t.Fatalf("Invalid container ID: %s", container1.Id)
t.Fatalf("Invalid container ID: %s", container1.ID)
}
}
@@ -46,7 +46,7 @@ func TestMultipleAttachRestart(t *testing.T) {
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c",
"i=1; while [ $i -le 5 ]; do i=`expr $i + 1`; echo hello; done"},
},
@@ -153,7 +153,7 @@ func TestDiff(t *testing.T) {
// Create a container and remove a file
container1, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/rm", "/etc/passwd"},
},
)
@@ -194,7 +194,7 @@ func TestDiff(t *testing.T) {
// Create a new container from the commited image
container2, err := builder.Create(
&Config{
Image: img.Id,
Image: img.ID,
Cmd: []string{"cat", "/etc/passwd"},
},
)
@@ -217,6 +217,37 @@ func TestDiff(t *testing.T) {
t.Fatalf("/etc/passwd should not be present in the diff after commit.")
}
}
// Create a new containere
container3, err := builder.Create(
&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"rm", "/bin/httpd"},
},
)
if err != nil {
t.Fatal(err)
}
defer runtime.Destroy(container3)
if err := container3.Run(); err != nil {
t.Fatal(err)
}
// Check the changelog
c, err = container3.Changes()
if err != nil {
t.Fatal(err)
}
success = false
for _, elem := range c {
if elem.Path == "/bin/httpd" && elem.Kind == 2 {
success = true
}
}
if !success {
t.Fatalf("/bin/httpd should be present in the diff after commit.")
}
}
func TestCommitAutoRun(t *testing.T) {
@@ -229,7 +260,7 @@ func TestCommitAutoRun(t *testing.T) {
builder := NewBuilder(runtime)
container1, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
},
)
@@ -260,7 +291,7 @@ func TestCommitAutoRun(t *testing.T) {
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
container2, err := builder.Create(
&Config{
Image: img.Id,
Image: img.ID,
},
)
if err != nil {
@@ -309,7 +340,7 @@ func TestCommitRun(t *testing.T) {
container1, err := builder.Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
},
)
@@ -341,7 +372,7 @@ func TestCommitRun(t *testing.T) {
container2, err := builder.Create(
&Config{
Image: img.Id,
Image: img.ID,
Cmd: []string{"cat", "/world"},
},
)
@@ -388,7 +419,7 @@ func TestStart(t *testing.T) {
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Memory: 33554432,
CpuShares: 1000,
Cmd: []string{"/bin/cat"},
@@ -432,7 +463,7 @@ func TestRun(t *testing.T) {
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
)
@@ -460,7 +491,7 @@ func TestOutput(t *testing.T) {
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(
&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"},
},
)
@@ -484,7 +515,7 @@ func TestKillDifferentUser(t *testing.T) {
}
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"tail", "-f", "/etc/resolv.conf"},
User: "daemon",
},
@@ -532,7 +563,7 @@ func TestKill(t *testing.T) {
}
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat", "/dev/zero"},
},
)
@@ -580,7 +611,7 @@ func TestExitCode(t *testing.T) {
builder := NewBuilder(runtime)
trueContainer, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true", ""},
})
if err != nil {
@@ -595,7 +626,7 @@ func TestExitCode(t *testing.T) {
}
falseContainer, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/false", ""},
})
if err != nil {
@@ -617,7 +648,7 @@ func TestRestart(t *testing.T) {
}
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"},
},
)
@@ -650,7 +681,7 @@ func TestRestartStdin(t *testing.T) {
}
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
OpenStdin: true,
@@ -732,7 +763,7 @@ func TestUser(t *testing.T) {
// Default user must be root
container, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
},
)
@@ -750,7 +781,7 @@ func TestUser(t *testing.T) {
// Set a username
container, err = builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
User: "root",
@@ -770,7 +801,7 @@ func TestUser(t *testing.T) {
// Set a UID
container, err = builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
User: "0",
@@ -790,7 +821,7 @@ func TestUser(t *testing.T) {
// Set a different user by uid
container, err = builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
User: "1",
@@ -812,7 +843,7 @@ func TestUser(t *testing.T) {
// Set a different user by username
container, err = builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
User: "daemon",
@@ -841,7 +872,7 @@ func TestMultipleContainers(t *testing.T) {
builder := NewBuilder(runtime)
container1, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat", "/dev/zero"},
},
)
@@ -851,7 +882,7 @@ func TestMultipleContainers(t *testing.T) {
defer runtime.Destroy(container1)
container2, err := builder.Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat", "/dev/zero"},
},
)
@@ -897,7 +928,7 @@ func TestStdin(t *testing.T) {
}
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
OpenStdin: true,
@@ -944,7 +975,7 @@ func TestTty(t *testing.T) {
}
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
OpenStdin: true,
@@ -991,7 +1022,7 @@ func TestEnv(t *testing.T) {
}
defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/usr/bin/env"},
},
)
@@ -1069,7 +1100,7 @@ func TestLXCConfig(t *testing.T) {
cpuMax := 10000
cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true"},
Hostname: "foobar",
@@ -1097,7 +1128,7 @@ func BenchmarkRunSequencial(b *testing.B) {
defer nuke(runtime)
for i := 0; i < b.N; i++ {
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"},
},
)
@@ -1132,7 +1163,7 @@ func BenchmarkRunParallel(b *testing.B) {
tasks = append(tasks, complete)
go func(i int, complete chan error) {
container, err := NewBuilder(runtime).Create(&Config{
Image: GetTestImage(runtime).Id,
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"},
},
)

View File

@@ -11,13 +11,13 @@ import (
"time"
)
var DOCKER_PATH string = path.Join(os.Getenv("DOCKERPATH"), "docker")
var DOCKERPATH = 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")
cmd := exec.Command(DOCKERPATH, "-d")
outPipe, err := cmd.StdoutPipe()
if err != nil {
return nil, err
@@ -77,7 +77,7 @@ func crashTest() error {
stop = false
for i := 0; i < 100 && !stop; {
func() error {
cmd := exec.Command(DOCKER_PATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
cmd := exec.Command(DOCKERPATH, "run", "base", "echo", fmt.Sprintf("%d", totalTestCount))
i++
totalTestCount++
outPipe, err := cmd.StdoutPipe()
@@ -116,7 +116,6 @@ func crashTest() error {
return err
}
}
return nil
}
func main() {

View File

@@ -1 +0,0 @@
Solomon Hykes <solomon@dotcloud.com>

View File

@@ -1,68 +0,0 @@
# docker-build: build your software with docker
## Description
docker-build is a script to build docker images from source. It will be deprecated once the 'build' feature is incorporated into docker itself (See https://github.com/dotcloud/docker/issues/278)
Author: Solomon Hykes <solomon@dotcloud.com>
## Install
docker-builder requires:
1) A reasonably recent Python setup (tested on 2.7.2).
2) A running docker daemon at version 0.1.4 or more recent (http://www.docker.io/gettingstarted)
## Usage
First create a valid Changefile, which defines a sequence of changes to apply to a base image.
$ cat Changefile
# Start build from a know base image
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
# Insert files from the host (./myscript must be present in the current directory)
copy myscript /usr/local/bin/myscript
Run docker-build, and pass the contents of your Changefile as standard input.
$ IMG=$(./docker-build < Changefile)
This will take a while: for each line of the changefile, docker-build will:
1. Create a new container to execute the given command or insert the given file
2. Wait for the container to complete execution
3. Commit the resulting changes as a new image
4. Use the resulting image as the input of the next step
If all the steps succeed, the result will be an image containing the combined results of each build step.
You can trace back those build steps by inspecting the image's history:
$ docker history $IMG
ID CREATED CREATED BY
1e9e2045de86 A few seconds ago /bin/sh -c cat > /usr/local/bin/myscript; chmod +x /usr/local/bin/git
77db140aa62a A few seconds ago /bin/sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang
77db140aa62a A few seconds ago /bin/sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
77db140aa62a A few seconds ago /bin/sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
83e85d155451 A few seconds ago /bin/sh -c apt-get update
bfd53b36d9d3 A few seconds ago /bin/sh -c echo 'deb http://archive.ubuntu.com/ubuntu quantal main universe multiverse' > /etc/apt/sources.list
base 2 weeks ago /bin/bash
27cf78414709 2 weeks ago
Note that your build started from 'base', as instructed by your Changefile. But that base image itself seems to have been built in 2 steps - hence the extra step in the history.
You can use this build technique to create any image you want: a database, a web application, or anything else that can be build by a sequence of unix commands - in other words, anything else.

View File

@@ -1,142 +0,0 @@
#!/usr/bin/env python
# docker-build is a script to build docker images from source.
# It will be deprecated once the 'build' feature is incorporated into docker itself.
# (See https://github.com/dotcloud/docker/issues/278)
#
# Author: Solomon Hykes <solomon@dotcloud.com>
# First create a valid Changefile, which defines a sequence of changes to apply to a base image.
#
# $ cat Changefile
# # Start build from a know base image
# 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
# # Insert files from the host (./myscript must be present in the current directory)
# copy myscript /usr/local/bin/myscript
#
#
# Run docker-build, and pass the contents of your Changefile as standard input.
#
# $ IMG=$(./docker-build < Changefile)
#
# This will take a while: for each line of the changefile, docker-build will:
#
# 1. Create a new container to execute the given command or insert the given file
# 2. Wait for the container to complete execution
# 3. Commit the resulting changes as a new image
# 4. Use the resulting image as the input of the next step
import sys
import subprocess
import json
import hashlib
def docker(args, stdin=None):
print "# docker " + " ".join(args)
p = subprocess.Popen(["docker"] + list(args), stdin=stdin, stdout=subprocess.PIPE)
return p.stdout
def image_exists(img):
return docker(["inspect", img]).read().strip() != ""
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"] + (["-author", author] if author else []) + (["-run", json.dumps(run)] if run is not None else []) + [run_id]).read().rstrip()
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, 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():
line = line.strip()
# Skip comments and empty lines
if line == "" or line[0] == "#":
continue
op, param = line.split(None, 1)
print op.upper() + " " + param
if op == "from":
base = param
steps.append(base)
elif op == "maintainer":
maintainer = param
elif op == "run":
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, 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:
docker(["rmi"] + steps[1:])
raise
print base
if __name__ == "__main__":
main()

View File

@@ -1,13 +0,0 @@
# 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
# Insert files from the host (./myscript must be present in the current directory)
copy myscript /usr/local/bin/myscript
push /src

View File

@@ -1,3 +0,0 @@
#!/bin/sh
echo hello, world!

View File

@@ -8,7 +8,7 @@
echo "Ensuring basic dependencies are installed..."
apt-get -qq update
apt-get -qq install lxc wget bsdtar
apt-get -qq install lxc wget
echo "Looking in /proc/filesystems to see if we have AUFS support..."
if grep -q aufs /proc/filesystems

View File

@@ -2,18 +2,15 @@
set -e
# these should match the names found at http://www.debian.org/releases/
stableSuite='squeeze'
testingSuite='wheezy'
stableSuite='wheezy'
testingSuite='jessie'
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}"
suite="${2:-$stableSuite}"
mirror="${3:-}" # stick to the default debootstrap mirror if one is not provided
if [ ! "$repo" ]; then
@@ -41,17 +38,14 @@ 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
if [ "$suite" = "$stableSuite" -o "$suite" = 'stable' ]; then
# tag latest
docker tag $img $repo latest
# tag the specific debian release version
ver=$(docker run $repo:$suite cat /etc/debian_version)
docker tag $img $repo $ver
fi

View File

@@ -15,7 +15,7 @@ import (
)
var (
GIT_COMMIT string
GITCOMMIT string
)
func main() {
@@ -24,53 +24,49 @@ func main() {
docker.SysInit()
return
}
host := "127.0.0.1"
port := 4243
// 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")
flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to")
flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
flDns := flag.String("dns", "", "Set custom dns servers")
flHosts := docker.ListOpts{fmt.Sprintf("tcp://%s:%d", docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT)}
flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use")
flag.Parse()
if len(flHosts) > 1 {
flHosts = flHosts[1:len(flHosts)] //trick to display a nice defaul value in the usage
}
for i, flHost := range flHosts {
flHosts[i] = utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost)
}
if *bridgeName != "" {
docker.NetworkBridgeIface = *bridgeName
} else {
docker.NetworkBridgeIface = docker.DefaultNetworkBridge
}
if strings.Contains(*flHost, ":") {
hostParts := strings.Split(*flHost, ":")
if len(hostParts) != 2 {
log.Fatal("Invalid bind address format.")
os.Exit(-1)
}
if hostParts[0] != "" {
host = hostParts[0]
}
if p, err := strconv.Atoi(hostParts[1]); err == nil {
port = p
}
} else {
host = *flHost
}
if *flDebug {
os.Setenv("DEBUG", "1")
}
docker.GIT_COMMIT = GIT_COMMIT
docker.GITCOMMIT = GITCOMMIT
if *flDaemon {
if flag.NArg() != 0 {
flag.Usage()
return
}
if err := daemon(*pidfile, host, port, *flAutoRestart); err != nil {
if err := daemon(*pidfile, flHosts, *flAutoRestart, *flEnableCors, *flDns); err != nil {
log.Fatal(err)
os.Exit(-1)
}
} else {
if err := docker.ParseCommands(host, port, flag.Args()...); err != nil {
if len(flHosts) > 1 {
log.Fatal("Please specify only one -H")
return
}
protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
log.Fatal(err)
os.Exit(-1)
}
@@ -104,10 +100,7 @@ func removePidFile(pidfile string) {
}
}
func daemon(pidfile, addr string, port int, autoRestart bool) error {
if addr != "127.0.0.1" {
log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
}
func daemon(pidfile string, protoAddrs []string, autoRestart, enableCors bool, flDns string) error {
if err := createPidFile(pidfile); err != nil {
log.Fatal(err)
}
@@ -121,11 +114,36 @@ func daemon(pidfile, addr string, port int, autoRestart bool) error {
removePidFile(pidfile)
os.Exit(0)
}()
server, err := docker.NewServer(autoRestart)
var dns []string
if flDns != "" {
dns = []string{flDns}
}
server, err := docker.NewServer(autoRestart, enableCors, dns)
if err != nil {
return err
}
return docker.ListenAndServe(fmt.Sprintf("%s:%d", addr, port), server, true)
chErrors := make(chan error, len(protoAddrs))
for _, protoAddr := range protoAddrs {
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
if protoAddrParts[0] == "unix" {
syscall.Unlink(protoAddrParts[1])
} else if protoAddrParts[0] == "tcp" {
if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") {
log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
}
} else {
log.Fatal("Invalid protocol format.")
os.Exit(-1)
}
go func() {
chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true)
}()
}
for i := 0; i < len(protoAddrs); i += 1 {
err := <-chErrors
if err != nil {
return err
}
}
return nil
}

View File

@@ -6,6 +6,7 @@ SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
PYTHON = python
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
@@ -38,17 +39,19 @@ help:
# @echo " linkcheck to check all external links for integrity"
# @echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " docs to build the docs and copy the static files to the outputdir"
@echo " server to serve the docs in your browser under \`http://localhost:8000\`"
@echo " publish to publish the app to dotcloud"
clean:
-rm -rf $(BUILDDIR)/*
docs:
#-rm -rf $(BUILDDIR)/*
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The documentation pages are now in $(BUILDDIR)/html."
server: docs
@cd $(BUILDDIR)/html; $(PYTHON) -m SimpleHTTPServer 8000
site:
cp -r website $(BUILDDIR)/
@@ -58,12 +61,13 @@ site:
connect:
@echo connecting dotcloud to www.docker.io website, make sure to use user 1
@cd _build/website/ ; \
dotcloud connect dockerwebsite ;
@echo or create your own "dockerwebsite" app
@cd $(BUILDDIR)/website/ ; \
dotcloud connect dockerwebsite ; \
dotcloud list
push:
@cd _build/website/ ; \
@cd $(BUILDDIR)/website/ ; \
dotcloud push
$(VERSIONS):

View File

@@ -14,20 +14,22 @@ Installation
------------
* Work in your own fork of the code, we accept pull requests.
* Install sphinx: ``pip install sphinx``
* Install sphinx httpdomain contrib package ``sphinxcontrib-httpdomain``
* Install sphinx: `pip install sphinx`
* Mac OS X: `[sudo] pip-2.7 install sphinx`)
* Install sphinx httpdomain contrib package: `pip install sphinxcontrib-httpdomain`
* Mac OS X: `[sudo] pip-2.7 install sphinxcontrib-httpdomain`
* If pip is not available you can probably install it using your favorite package manager as **python-pip**
Usage
-----
* change the .rst files with your favorite editor to your liking
* run *make docs* to clean up old files and generate new ones
* your static website can now be found in the _build dir
* to preview what you have generated, cd into _build/html and then run 'python -m SimpleHTTPServer 8000'
* Change the `.rst` files with your favorite editor to your liking.
* Run `make docs` to clean up old files and generate new ones.
* Your static website can now be found in the `_build` directory.
* To preview what you have generated run `make server` and open <http://localhost:8000/> in your favorite browser.
Working using github's file editor
Working using GitHub's file editor
----------------------------------
Alternatively, for small changes and typo's you might want to use github's built in file editor. It allows
Alternatively, for small changes and typo's you might want to use GitHub's built in file editor. It allows
you to preview your changes right online. Just be carefull not to create many commits.
Images
@@ -72,4 +74,4 @@ Guides on using sphinx
* Code examples
Start without $, so it's easy to copy and paste.
Start without $, so it's easy to copy and paste.

View File

@@ -0,0 +1,5 @@
This directory holds the authoritative specifications of APIs defined and implemented by Docker. Currently this includes:
* The remote API by which a docker node can be queried over HTTP
* The registry API by which a docker node can download and upload container images for storage and sharing
* The index search API by which a docker node can search the public index for images to download

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,16 +2,17 @@
:description: docker documentation
:keywords: docker, ipa, documentation
API's
=============
APIs
====
This following :
Your programs and scripts can access Docker's functionality via these interfaces:
.. toctree::
:maxdepth: 3
registry_index_spec
registry_api
index_search_api
index_api
docker_remote_api

View File

@@ -0,0 +1,553 @@
:title: Index API
:description: API Documentation for Docker Index
:keywords: API, Docker, index, REST, documentation
=================
Docker Index API
=================
.. contents:: Table of Contents
1. Brief introduction
=====================
- This is the REST API for the Docker index
- Authorization is done with basic auth over SSL
- Not all commands require authentication, only those noted as such.
2. Endpoints
============
2.1 Repository
^^^^^^^^^^^^^^
Repositories
*************
User Repo
~~~~~~~~~
.. http:put:: /v1/repositories/(namespace)/(repo_name)/
Create a user repository with the given ``namespace`` and ``repo_name``.
**Example Request**:
.. sourcecode:: http
PUT /v1/repositories/foo/bar/ HTTP/1.1
Host: index.docker.io
Accept: application/json
Content-Type: application/json
Authorization: Basic akmklmasadalkm==
X-Docker-Token: true
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}]
:parameter namespace: the namespace for the repo
:parameter repo_name: the name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=write
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
""
:statuscode 200: Created
:statuscode 400: Errors (invalid json, missing or invalid fields, etc)
:statuscode 401: Unauthorized
:statuscode 403: Account is not Active
.. http:delete:: /v1/repositories/(namespace)/(repo_name)/
Delete a user repository with the given ``namespace`` and ``repo_name``.
**Example Request**:
.. sourcecode:: http
DELETE /v1/repositories/foo/bar/ HTTP/1.1
Host: index.docker.io
Accept: application/json
Content-Type: application/json
Authorization: Basic akmklmasadalkm==
X-Docker-Token: true
""
:parameter namespace: the namespace for the repo
:parameter repo_name: the name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 202
Vary: Accept
Content-Type: application/json
WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=delete
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
""
:statuscode 200: Deleted
:statuscode 202: Accepted
:statuscode 400: Errors (invalid json, missing or invalid fields, etc)
:statuscode 401: Unauthorized
:statuscode 403: Account is not Active
Library Repo
~~~~~~~~~~~~
.. http:put:: /v1/repositories/(repo_name)/
Create a library repository with the given ``repo_name``.
This is a restricted feature only available to docker admins.
When namespace is missing, it is assumed to be ``library``
**Example Request**:
.. sourcecode:: http
PUT /v1/repositories/foobar/ HTTP/1.1
Host: index.docker.io
Accept: application/json
Content-Type: application/json
Authorization: Basic akmklmasadalkm==
X-Docker-Token: true
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”}]
:parameter repo_name: the library name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
WWW-Authenticate: Token signature=123abc,repository=”library/foobar”,access=write
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
""
:statuscode 200: Created
:statuscode 400: Errors (invalid json, missing or invalid fields, etc)
:statuscode 401: Unauthorized
:statuscode 403: Account is not Active
.. http:delete:: /v1/repositories/(repo_name)/
Delete a library repository with the given ``repo_name``.
This is a restricted feature only available to docker admins.
When namespace is missing, it is assumed to be ``library``
**Example Request**:
.. sourcecode:: http
DELETE /v1/repositories/foobar/ HTTP/1.1
Host: index.docker.io
Accept: application/json
Content-Type: application/json
Authorization: Basic akmklmasadalkm==
X-Docker-Token: true
""
:parameter repo_name: the library name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 202
Vary: Accept
Content-Type: application/json
WWW-Authenticate: Token signature=123abc,repository=”library/foobar”,access=delete
X-Docker-Endpoints: registry-1.docker.io [, registry-2.docker.io]
""
:statuscode 200: Deleted
:statuscode 202: Accepted
:statuscode 400: Errors (invalid json, missing or invalid fields, etc)
:statuscode 401: Unauthorized
:statuscode 403: Account is not Active
Repository Images
*****************
User Repo Images
~~~~~~~~~~~~~~~~
.. http:put:: /v1/repositories/(namespace)/(repo_name)/images
Update the images for a user repo.
**Example Request**:
.. sourcecode:: http
PUT /v1/repositories/foo/bar/images HTTP/1.1
Host: index.docker.io
Accept: application/json
Content-Type: application/json
Authorization: Basic akmklmasadalkm==
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
“checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
:parameter namespace: the namespace for the repo
:parameter repo_name: the name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 204
Vary: Accept
Content-Type: application/json
""
:statuscode 204: Created
:statuscode 400: Errors (invalid json, missing or invalid fields, etc)
:statuscode 401: Unauthorized
:statuscode 403: Account is not Active or permission denied
.. http:get:: /v1/repositories/(namespace)/(repo_name)/images
get the images for a user repo.
**Example Request**:
.. sourcecode:: http
GET /v1/repositories/foo/bar/images HTTP/1.1
Host: index.docker.io
Accept: application/json
:parameter namespace: the namespace for the repo
:parameter repo_name: the name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
“checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”},
{“id”: “ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds”,
“checksum”: “34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew”}]
:statuscode 200: OK
:statuscode 404: Not found
Library Repo Images
~~~~~~~~~~~~~~~~~~~
.. http:put:: /v1/repositories/(repo_name)/images
Update the images for a library repo.
**Example Request**:
.. sourcecode:: http
PUT /v1/repositories/foobar/images HTTP/1.1
Host: index.docker.io
Accept: application/json
Content-Type: application/json
Authorization: Basic akmklmasadalkm==
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
“checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
:parameter repo_name: the library name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 204
Vary: Accept
Content-Type: application/json
""
:statuscode 204: Created
:statuscode 400: Errors (invalid json, missing or invalid fields, etc)
:statuscode 401: Unauthorized
:statuscode 403: Account is not Active or permission denied
.. http:get:: /v1/repositories/(repo_name)/images
get the images for a library repo.
**Example Request**:
.. sourcecode:: http
GET /v1/repositories/foobar/images HTTP/1.1
Host: index.docker.io
Accept: application/json
:parameter repo_name: the library name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
“checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”},
{“id”: “ertwetewtwe38722009fe6857087b486531f9a779a0c1dfddgfgsdgdsgds”,
“checksum”: “34t23f23fc17e3ed29dae8f12c4f9e89cc6f0bsdfgfsdgdsgdsgerwgew”}]
:statuscode 200: OK
:statuscode 404: Not found
Repository Authorization
************************
Library Repo
~~~~~~~~~~~~
.. http:put:: /v1/repositories/(repo_name)/auth
authorize a token for a library repo
**Example Request**:
.. sourcecode:: http
PUT /v1/repositories/foobar/auth HTTP/1.1
Host: index.docker.io
Accept: application/json
Authorization: Token signature=123abc,repository="library/foobar",access=write
:parameter repo_name: the library name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
"OK"
:statuscode 200: OK
:statuscode 403: Permission denied
:statuscode 404: Not found
User Repo
~~~~~~~~~
.. http:put:: /v1/repositories/(namespace)/(repo_name)/auth
authorize a token for a user repo
**Example Request**:
.. sourcecode:: http
PUT /v1/repositories/foo/bar/auth HTTP/1.1
Host: index.docker.io
Accept: application/json
Authorization: Token signature=123abc,repository="foo/bar",access=write
:parameter namespace: the namespace for the repo
:parameter repo_name: the name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
"OK"
:statuscode 200: OK
:statuscode 403: Permission denied
:statuscode 404: Not found
2.2 Users
^^^^^^^^^
User Login
**********
.. http:get:: /v1/users
If you want to check your login, you can try this endpoint
**Example Request**:
.. sourcecode:: http
GET /v1/users HTTP/1.1
Host: index.docker.io
Accept: application/json
Authorization: Basic akmklmasadalkm==
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
OK
:statuscode 200: no error
:statuscode 401: Unauthorized
:statuscode 403: Account is not Active
User Register
*************
.. http:post:: /v1/users
Registering a new account.
**Example request**:
.. sourcecode:: http
POST /v1/users HTTP/1.1
Host: index.docker.io
Accept: application/json
Content-Type: application/json
{"email": "sam@dotcloud.com",
"password": "toto42",
"username": "foobar"'}
:jsonparameter email: valid email address, that needs to be confirmed
:jsonparameter username: min 4 character, max 30 characters, must match the regular expression [a-z0-9_].
:jsonparameter password: min 5 characters
**Example Response**:
.. sourcecode:: http
HTTP/1.1 201 OK
Vary: Accept
Content-Type: application/json
"User Created"
:statuscode 201: User Created
:statuscode 400: Errors (invalid json, missing or invalid fields, etc)
Update User
***********
.. http:put:: /v1/users/(username)/
Change a password or email address for given user. If you pass in an email,
it will add it to your account, it will not remove the old one. Passwords will
be updated.
It is up to the client to verify that that password that is sent is the one that
they want. Common approach is to have them type it twice.
**Example Request**:
.. sourcecode:: http
PUT /v1/users/fakeuser/ HTTP/1.1
Host: index.docker.io
Accept: application/json
Content-Type: application/json
Authorization: Basic akmklmasadalkm==
{"email": "sam@dotcloud.com",
"password": "toto42"}
:parameter username: username for the person you want to update
**Example Response**:
.. sourcecode:: http
HTTP/1.1 204
Vary: Accept
Content-Type: application/json
""
:statuscode 204: User Updated
:statuscode 400: Errors (invalid json, missing or invalid fields, etc)
:statuscode 401: Unauthorized
:statuscode 403: Account is not Active
:statuscode 404: User not found
2.3 Search
^^^^^^^^^^
If you need to search the index, this is the endpoint you would use.
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

View File

@@ -1,43 +0,0 @@
:title: Docker Index documentation
:description: Documentation for docker Index
:keywords: docker, index, api
=======================
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

View File

@@ -1,7 +1,6 @@
:title: Registry Documentation
:description: Documentation for docker Registry and Registry API
:keywords: docker, registry, api, index
:title: Registry API
:description: API Documentation for Docker Registry
:keywords: API, Docker, index, registry, REST, documentation
===================
Docker Registry API
@@ -9,29 +8,10 @@ Docker Registry API
.. contents:: Table of Contents
1. The 3 roles
===============
1. Brief introduction
=====================
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
------------
- This is the REST API for the Docker 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
@@ -60,419 +40,424 @@ We expect that there will be multiple registries out there. To help to grasp the
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
2. Endpoints
============
2.1 Images
----------
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
Layer
*****
2. Workflow
===========
.. http:get:: /v1/images/(image_id)/layer
2.1 Pull
get image layer for a given ``image_id``
**Example Request**:
.. sourcecode:: http
GET /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/layer HTTP/1.1
Host: registry-1.docker.io
Accept: application/json
Content-Type: application/json
Authorization: Token akmklmasadalkmsdfgsdgdge33
:parameter image_id: the id for the layer you want to get
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
Cookie: (Cookie provided by the Registry)
{
id: "088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c",
parent: "aeee6396d62273d180a49c96c62e45438d87c7da4a5cf5d2be6bee4e21bc226f",
created: "2013-04-30T17:46:10.843673+03:00",
container: "8305672a76cc5e3d168f97221106ced35a76ec7ddbb03209b0f0d96bf74f6ef7",
container_config: {
Hostname: "host-test",
User: "",
Memory: 0,
MemorySwap: 0,
AttachStdin: false,
AttachStdout: false,
AttachStderr: false,
PortSpecs: null,
Tty: false,
OpenStdin: false,
StdinOnce: false,
Env: null,
Cmd: [
"/bin/bash",
"-c",
"apt-get -q -yy -f install libevent-dev"
],
Dns: null,
Image: "imagename/blah",
Volumes: { },
VolumesFrom: ""
},
docker_version: "0.1.7"
}
:statuscode 200: OK
:statuscode 401: Requires authorization
:statuscode 404: Image not found
.. http:put:: /v1/images/(image_id)/layer
put image layer for a given ``image_id``
**Example Request**:
.. sourcecode:: http
PUT /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/layer HTTP/1.1
Host: registry-1.docker.io
Accept: application/json
Content-Type: application/json
Authorization: Token akmklmasadalkmsdfgsdgdge33
{
id: "088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c",
parent: "aeee6396d62273d180a49c96c62e45438d87c7da4a5cf5d2be6bee4e21bc226f",
created: "2013-04-30T17:46:10.843673+03:00",
container: "8305672a76cc5e3d168f97221106ced35a76ec7ddbb03209b0f0d96bf74f6ef7",
container_config: {
Hostname: "host-test",
User: "",
Memory: 0,
MemorySwap: 0,
AttachStdin: false,
AttachStdout: false,
AttachStderr: false,
PortSpecs: null,
Tty: false,
OpenStdin: false,
StdinOnce: false,
Env: null,
Cmd: [
"/bin/bash",
"-c",
"apt-get -q -yy -f install libevent-dev"
],
Dns: null,
Image: "imagename/blah",
Volumes: { },
VolumesFrom: ""
},
docker_version: "0.1.7"
}
:parameter image_id: the id for the layer you want to get
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
""
:statuscode 200: OK
:statuscode 401: Requires authorization
:statuscode 404: Image not found
Image
*****
.. http:put:: /v1/images/(image_id)/json
put image for a given ``image_id``
**Example Request**:
.. sourcecode:: http
PUT /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/json HTTP/1.1
Host: registry-1.docker.io
Accept: application/json
Content-Type: application/json
Cookie: (Cookie provided by the Registry)
{
“id”: “088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c”,
“checksum”: “sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
}
:parameter image_id: the id for the layer you want to get
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
""
:statuscode 200: OK
:statuscode 401: Requires authorization
.. http:get:: /v1/images/(image_id)/json
get image for a given ``image_id``
**Example Request**:
.. sourcecode:: http
GET /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/json HTTP/1.1
Host: registry-1.docker.io
Accept: application/json
Content-Type: application/json
Cookie: (Cookie provided by the Registry)
:parameter image_id: the id for the layer you want to get
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
{
“id”: “088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c”,
“checksum”: “sha256:b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
}
:statuscode 200: OK
:statuscode 401: Requires authorization
:statuscode 404: Image not found
Ancestry
********
.. http:get:: /v1/images/(image_id)/ancestry
get ancestry for an image given an ``image_id``
**Example Request**:
.. sourcecode:: http
GET /v1/images/088b4505aa3adc3d35e79c031fa126b403200f02f51920fbd9b7c503e87c7a2c/ancestry HTTP/1.1
Host: registry-1.docker.io
Accept: application/json
Content-Type: application/json
Cookie: (Cookie provided by the Registry)
:parameter image_id: the id for the layer you want to get
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
["088b4502f51920fbd9b7c503e87c7a2c05aa3adc3d35e79c031fa126b403200f",
"aeee63968d87c7da4a5cf5d2be6bee4e21bc226fd62273d180a49c96c62e4543",
"bfa4c5326bc764280b0863b46a4b20d940bc1897ef9c1dfec060604bdc383280",
"6ab5893c6927c15a15665191f2c6cf751f5056d8b95ceee32e43c5e8a3648544"]
:statuscode 200: OK
:statuscode 401: Requires authorization
:statuscode 404: Image not found
2.2 Tags
--------
.. image:: /static_files/docker_pull_chart.png
.. http:get:: /v1/repositories/(namespace)/(repository)/tags
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
get all of the tags for the given repo.
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.
**Example Request**:
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.
.. sourcecode:: http
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**:
GET /v1/repositories/foo/bar/tags HTTP/1.1
Host: registry-1.docker.io
Accept: application/json
Content-Type: application/json
Cookie: (Cookie provided by the Registry)
7. (Docker -> Registry) PUT /v1/images/98765432_parent/layer
**Headers**:
:parameter namespace: namespace for the repo
:parameter repository: name for the repo
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
{
"latest": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
“0.1.1”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”
}
:statuscode 200: OK
:statuscode 401: Requires authorization
:statuscode 404: Repository not found
.. http:get:: /v1/repositories/(namespace)/(repository)/tags/(tag)
get a tag for the given repo.
**Example Request**:
.. sourcecode:: http
GET /v1/repositories/foo/bar/tags/latest HTTP/1.1
Host: registry-1.docker.io
Accept: application/json
Content-Type: application/json
Cookie: (Cookie provided by the Registry)
8. (Docker -> Registry) PUT /v1/images/98765432/layer
**Headers**:
X-Docker-Checksum: sha256:436745873465fdjkhdfjkgh
:parameter namespace: namespace for the repo
:parameter repository: name for the repo
:parameter tag: name of tag you want to get
9. (Docker -> Registry) PUT /v1/repositories/foo/bar/tags/latest
**Headers**:
**Example Response**:
.. sourcecode:: http
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
"9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"
:statuscode 200: OK
:statuscode 401: Requires authorization
:statuscode 404: Tag not found
.. http:delete:: /v1/repositories/(namespace)/(repository)/tags/(tag)
delete the tag for the repo
**Example Request**:
.. sourcecode:: http
DELETE /v1/repositories/foo/bar/tags/latest HTTP/1.1
Host: registry-1.docker.io
Accept: application/json
Content-Type: application/json
Cookie: (Cookie provided by the Registry)
**Body**:
“98765432”
10. (Docker -> Index) PUT /v1/repositories/foo/bar/images
:parameter namespace: namespace for the repo
:parameter repository: name for the repo
:parameter tag: name of tag you want to delete
**Headers**:
Authorization: Basic 123oislifjsldfj==
X-Docker-Endpoints: registry1.docker.io (no validation on this right now)
**Example Response**:
**Body**:
(The image, ids, tags and checksums)
.. sourcecode:: http
[{“id”: “9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”,
“checksum”: “b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087”}]
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
**Return** HTTP 204
""
.. note::
:statuscode 200: OK
:statuscode 401: Requires authorization
:statuscode 404: Tag not found
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.
.. http:put:: /v1/repositories/(namespace)/(repository)/tags/(tag)
3. How to use the Registry in standalone mode
=============================================
put a tag for the given repo.
The Index has two main purposes (along with its fancy social features):
**Example Request**:
- 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)
.. sourcecode:: http
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.
PUT /v1/repositories/foo/bar/tags/latest HTTP/1.1
Host: registry-1.docker.io
Accept: application/json
Content-Type: application/json
Cookie: (Cookie provided by the Registry)
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...).
“9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f”
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.
:parameter namespace: namespace for the repo
:parameter repository: name for the repo
:parameter tag: name of tag you want to add
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).
**Example Response**:
3.2 With an Index
-----------------
.. sourcecode:: http
The Index data needed by the Registry are simple:
- Serve the checksums
- Provide and authorize a Token
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
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.
:statuscode 200: OK
:statuscode 400: Invalid data
:statuscode 401: Requires authorization
:statuscode 404: Image not found
4. The API
==========
2.3 Repositories
----------------
The first version of the api is available here: https://github.com/jpetazzo/docker/blob/acd51ecea8f5d3c02b00a08176171c59442df8b3/docs/images-repositories-push-pull.md
.. http:delete:: /v1/repositories/(namespace)/(repository)/
4.1 Images
----------
delete a repository
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.
**Example Request**:
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.
.. sourcecode:: http
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
DELETE /v1/repositories/foo/bar/ HTTP/1.1
Host: registry-1.docker.io
Accept: application/json
Content-Type: application/json
Cookie: (Cookie provided by the Registry)
4.2 Users
---------
""
4.2.1 Create a user (Index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
:parameter namespace: namespace for the repo
:parameter repository: name for the repo
POST /v1/users
**Example Response**:
**Body**:
{"email": "sam@dotcloud.com", "password": "toto42", "username": "foobar"'}
.. sourcecode:: http
**Validation**:
- **username** : min 4 character, max 30 characters, must match the regular expression [a-z0-9_].
- **password**: min 5 characters
HTTP/1.1 200
Vary: Accept
Content-Type: application/json
**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
:statuscode 200: OK
:statuscode 401: Requires authorization
:statuscode 404: Repository not found
.. note::
3.0 Authorization
=================
This is where we describe the authorization process, including the tokens and cookies.
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.
The following naming restrictions apply:
- Namespaces must match the same regular expression as usernames (See 4.2.1.)
- Repository names must match the regular expression [a-zA-Z0-9-_.]
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="
TODO: add more info.

View File

@@ -0,0 +1,569 @@
:title: Registry Documentation
:description: Documentation for docker Registry and Registry API
:keywords: docker, registry, api, index
=====================
Registry & index Spec
=====================
.. 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.
2.3 Delete
----------
If you need to delete something from the index or registry, we need a nice clean way to do that. Here is the workflow.
1. Docker contacts the index to request a delete of a repository “samalba/busybox” (authentication required with user credentials)
2. If authentication works and repository is valid, “samalba/busybox” is marked as deleted and a temporary token is returned
3. Send a delete request to the registry for the repository (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 deletes the repository and everything associated to it.
6. docker contacts the index to let it know it was removed from the registry, the index removes all records from the database.
.. note::
The Docker client should present an "Are you sure?" prompt to confirm the deletion before starting the process. Once it starts it can't be undone.
API (deleting repository foo/bar):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1. (Docker -> Index) DELETE /v1/repositories/foo/bar/
**Headers**:
Authorization: Basic sdkjfskdjfhsdkjfh==
X-Docker-Token: true
**Action**::
- in index, we make sure it is a valid repository, and set to deleted (logically)
**Body**::
Empty
2. (Index -> Docker) 202 Accepted
**Headers**:
- WWW-Authenticate: Token signature=123abc,repository=”foo/bar”,access=delete
- X-Docker-Endpoints: registry.docker.io [, registry2.docker.io] # list of endpoints where this repo lives.
3. (Docker -> Registry) DELETE /v1/repositories/foo/bar/
**Headers**:
Authorization: Token signature=123abc,repository=”foo/bar”,access=delete
4. (Registry->Index) PUT /v1/repositories/foo/bar/auth
**Headers**:
Authorization: Token signature=123abc,repository=”foo/bar”,access=delete
**Action**::
- Index:
will invalidate the token.
- Registry:
deletes the repository (if token is approved)
5. (Registry -> Docker) 200 OK
200 If success
403 if forbidden
400 if bad request
404 if repository isn't found
6. (Docker -> Index) DELETE /v1/repositories/foo/bar/
**Headers**:
Authorization: Basic 123oislifjsldfj==
X-Docker-Endpoints: registry-1.docker.io (no validation on this right now)
**Body**:
Empty
**Return** HTTP 200
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, must match the regular expression [a-z0-9_].
- **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.
The following naming restrictions apply:
- Namespaces must match the same regular expression as usernames (See 4.2.1.)
- Repository names must match the regular expression [a-zA-Z0-9-_.]
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
4.5 Repositories
----------------
4.5.1 Remove a Repository (Registry)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
DELETE /v1/repositories/<namespace>/<repo_name>
Return 200 OK
4.5.2 Remove a Repository (Index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This starts the delete process. see 2.3 for more details.
DELETE /v1/repositories/<namespace>/<repo_name>
Return 202 OK
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="
7.0 Document Version
---------------------
- 1.0 : May 6th 2013 : initial release
- 1.1 : June 1st 2013 : Added Delete Repository and way to handle new source namespace.

View File

@@ -15,7 +15,7 @@ To list available commands, either run ``docker`` with no parameters or execute
$ docker
Usage: docker [OPTIONS] COMMAND [arg...]
-H="127.0.0.1:4243": Host:port to bind/connect to
-H=[tcp://127.0.0.1:4243]: tcp://host:port to bind/connect to or unix://path/to/socket to use
A self-sufficient runtime for linux containers.

View File

@@ -8,9 +8,11 @@
::
Usage: docker build [OPTIONS] PATH | -
Usage: docker build [OPTIONS] PATH | URL | -
Build a new container image from the source code at PATH
-t="": Tag to be applied to the resulting image in case of success.
When a single Dockerfile is given as URL, then no context is set. When a git repository is set as URL, the repository is used as context
Examples
--------
@@ -19,10 +21,23 @@ Examples
docker build .
This will take the local Dockerfile
| This will read the Dockerfile from the current directory. It will also send any other files and directories found in the current directory to the docker daemon.
| The contents of this directory would be used by ADD commands found within the Dockerfile.
| This will send a lot of data to the docker daemon if the current directory contains a lot of data.
| If the absolute path is provided instead of '.', only the files and directories required by the ADD commands from the Dockerfile will be added to the context and transferred to the docker daemon.
|
.. code-block:: bash
docker build -
docker build - < Dockerfile
This will read a Dockerfile form Stdin without context
| This will read a Dockerfile from Stdin without context. Due to the lack of a context, no contents of any local directory will be sent to the docker daemon.
| ADD doesn't work when running in this mode due to the absence of the context, thus having no source files to copy to the container.
.. code-block:: bash
docker build github.com/creack/docker-firefox
| This will clone the github repository and use it as context. The Dockerfile at the root of the repository is used as Dockerfile.
| Note that you can specify an arbitrary git repository by using the 'git://' schema.

View File

@@ -8,6 +8,33 @@
::
Usage: docker import [OPTIONS] URL|- [REPOSITORY [TAG]]
Usage: docker import URL|- [REPOSITORY [TAG]]
Create a new filesystem image from the contents of a tarball
At this time, the URL must start with ``http`` and point to a single file archive (.tar, .tar.gz, .bzip)
containing a root filesystem. If you would like to import from a local directory or archive,
you can use the ``-`` parameter to take the data from standard in.
Examples
--------
Import from a remote location
.............................
``$ docker import http://example.com/exampleimage.tgz exampleimagerepo``
Import from a local file
........................
Import to docker via pipe and standard in
``$ cat exampleimage.tgz | docker import - exampleimagelocal``
Import from a local directory
.............................
``$ sudo tar -c . | docker import - exampleimagedir``
Note the ``sudo`` in this example -- you must preserve the ownership of the files (especially root ownership)
during the archiving with tar. If you are not root (or sudo) when you tar, then the ownerships might not get preserved.

View File

@@ -8,7 +8,7 @@
::
Usage: docker run [OPTIONS] IMAGE COMMAND [ARG...]
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Run a command in a new container

View File

@@ -1,8 +0,0 @@
:title: Introduction
:description: An introduction to docker and standard containers?
:keywords: containers, lxc, concepts, explanation, docker, documentation
:note: This version of the introduction is temporary, just to make sure we don't break the links from the website when the documentation is updated
This document has been moved to :ref:`introduction`, please update your bookmarks.

View File

@@ -1,125 +0,0 @@
:title: Introduction
:description: An introduction to docker and standard containers?
:keywords: containers, lxc, concepts, explanation
Introduction
============
Docker - The Linux container runtime
------------------------------------
Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
Docker is a great building block for automating distributed systems: large-scale web deployments, database clusters, continuous deployment systems, private PaaS, service-oriented architectures, etc.
- **Heterogeneous payloads** Any combination of binaries, libraries, configuration files, scripts, virtualenvs, jars, gems, tarballs, you name it. No more juggling between domain-specific tools. Docker can deploy and run them all.
- **Any server** Docker can run on any x64 machine with a modern linux kernel - whether it's a laptop, a bare metal server or a VM. This makes it perfect for multi-cloud deployments.
- **Isolation** docker isolates processes from each other and from the underlying host, using lightweight containers.
- **Repeatability** Because containers are isolated in their own filesystem, they behave the same regardless of where, when, and alongside what they run.
.. image:: images/lego_docker.jpg
What is a Standard Container?
-----------------------------
Docker defines a unit of software delivery called a Standard Container. The goal of a Standard Container is to encapsulate a software component and all its dependencies in
a format that is self-describing and portable, so that any compliant runtime can run it without extra dependency, regardless of the underlying machine and the contents of the container.
The spec for Standard Containers is currently work in progress, but it is very straightforward. It mostly defines 1) an image format, 2) a set of standard operations, and 3) an execution environment.
A great analogy for this is the shipping container. Just like Standard Containers are a fundamental unit of software delivery, shipping containers (http://bricks.argz.com/ins/7823-1/12) are a fundamental unit of physical delivery.
Standard operations
~~~~~~~~~~~~~~~~~~~
Just like shipping containers, Standard Containers define a set of STANDARD OPERATIONS. Shipping containers can be lifted, stacked, locked, loaded, unloaded and labelled. Similarly, standard containers can be started, stopped, copied, snapshotted, downloaded, uploaded and tagged.
Content-agnostic
~~~~~~~~~~~~~~~~~~~
Just like shipping containers, Standard Containers are CONTENT-AGNOSTIC: all standard operations have the same effect regardless of the contents. A shipping container will be stacked in exactly the same way whether it contains Vietnamese powder coffee or spare Maserati parts. Similarly, Standard Containers are started or uploaded in the same way whether they contain a postgres database, a php application with its dependencies and application server, or Java build artifacts.
Infrastructure-agnostic
~~~~~~~~~~~~~~~~~~~~~~~~~~
Both types of containers are INFRASTRUCTURE-AGNOSTIC: they can be transported to thousands of facilities around the world, and manipulated by a wide variety of equipment. A shipping container can be packed in a factory in Ukraine, transported by truck to the nearest routing center, stacked onto a train, loaded into a German boat by an Australian-built crane, stored in a warehouse at a US facility, etc. Similarly, a standard container can be bundled on my laptop, uploaded to S3, downloaded, run and snapshotted by a build server at Equinix in Virginia, uploaded to 10 staging servers in a home-made Openstack cluster, then sent to 30 production instances across 3 EC2 regions.
Designed for automation
~~~~~~~~~~~~~~~~~~~~~~~~~~
Because they offer the same standard operations regardless of content and infrastructure, Standard Containers, just like their physical counterpart, are extremely well-suited for automation. In fact, you could say automation is their secret weapon.
Many things that once required time-consuming and error-prone human effort can now be programmed. Before shipping containers, a bag of powder coffee was hauled, dragged, dropped, rolled and stacked by 10 different people in 10 different locations by the time it reached its destination. 1 out of 50 disappeared. 1 out of 20 was damaged. The process was slow, inefficient and cost a fortune - and was entirely different depending on the facility and the type of goods.
Similarly, before Standard Containers, by the time a software component ran in production, it had been individually built, configured, bundled, documented, patched, vendored, templated, tweaked and instrumented by 10 different people on 10 different computers. Builds failed, libraries conflicted, mirrors crashed, post-it notes were lost, logs were misplaced, cluster updates were half-broken. The process was slow, inefficient and cost a fortune - and was entirely different depending on the language and infrastructure provider.
Industrial-grade delivery
~~~~~~~~~~~~~~~~~~~~~~~~~~
There are 17 million shipping containers in existence, packed with every physical good imaginable. Every single one of them can be loaded on the same boats, by the same cranes, in the same facilities, and sent anywhere in the World with incredible efficiency. It is embarrassing to think that a 30 ton shipment of coffee can safely travel half-way across the World in *less time* than it takes a software team to deliver its code from one datacenter to another sitting 10 miles away.
With Standard Containers we can put an end to that embarrassment, by making INDUSTRIAL-GRADE DELIVERY of software a reality.
Standard Container Specification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(TODO)
Image format
~~~~~~~~~~~~
Standard operations
~~~~~~~~~~~~~~~~~~~
- Copy
- Run
- Stop
- Wait
- Commit
- Attach standard streams
- List filesystem changes
- ...
Execution environment
~~~~~~~~~~~~~~~~~~~~~
Root filesystem
^^^^^^^^^^^^^^^
Environment variables
^^^^^^^^^^^^^^^^^^^^^
Process arguments
^^^^^^^^^^^^^^^^^
Networking
^^^^^^^^^^
Process namespacing
^^^^^^^^^^^^^^^^^^^
Resource limits
^^^^^^^^^^^^^^^
Process monitoring
^^^^^^^^^^^^^^^^^^
Logging
^^^^^^^
Signals
^^^^^^^
Pseudo-terminal allocation
^^^^^^^^^^^^^^^^^^^^^^^^^^
Security
^^^^^^^^

View File

@@ -20,6 +20,20 @@ import sys, os
# -- General configuration -----------------------------------------------------
# Additional templates that should be rendered to pages, maps page names to
# template names.
# the 'redirect_home.html' page redirects using a http meta refresh which, according
# to official sources is more or less equivalent of a 301.
html_additional_pages = {
'concepts/containers': 'redirect_home.html',
'concepts/introduction': 'redirect_home.html',
}
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
@@ -120,7 +134,11 @@ html_theme_path = ['../theme']
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# We use a png favicon. This is not compatible with internet explorer, but looks
# much better on all other browsers. However, sphynx doesn't like it (it likes
# .ico better) so we have just put it in the template rather than used this setting
# html_favicon = 'favicon.png'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@@ -138,10 +156,6 @@ html_static_path = ['static_files']
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True

View File

@@ -5,5 +5,5 @@
Contributing to Docker
======================
Want to hack on Docker? Awesome! The repository includes `all the instructions you need to get started <https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`.
Want to hack on Docker? Awesome! The repository includes `all the instructions you need to get started <https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md>`_.

View File

@@ -1,15 +1,39 @@
:title: Setting up a dev environment
:title: Setting Up a Dev Environment
:description: Guides on how to contribute to docker
:keywords: Docker, documentation, developers, contributing, dev environment
Setting up a dev environment
Setting Up a Dev Environment
============================
Instructions that have been verified to work on Ubuntu 12.10,
Instructions that have been verified to work on Ubuntu Precise 12.04 (LTS) (64-bit),
Dependencies
------------
**Linux kernel 3.8**
Due to a bug in LXC docker works best on the 3.8 kernel. Precise comes with a 3.2 kernel, so we need to upgrade it. The kernel we install comes with AUFS built in.
.. code-block:: bash
sudo apt-get -y install lxc wget bsdtar curl golang git
# install the backported kernel
sudo apt-get update && sudo apt-get install linux-image-generic-lts-raring
# reboot
sudo reboot
Installation
------------
.. code-block:: bash
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:gophers/go
sudo apt-get update
sudo apt-get -y install lxc xz-utils curl golang-stable git aufs-tools
export GOPATH=~/go/
export PATH=$GOPATH/bin:$PATH

View File

@@ -4,8 +4,8 @@
.. _running_couchdb_service:
Create a CouchDB service
========================
CouchDB Service
===============
.. include:: example_header.inc

View File

@@ -1,6 +1,6 @@
:title: Docker Examples
:description: Examples on how to use Docker
:keywords: docker, hello world, examples
:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples
@@ -16,6 +16,7 @@ Contents:
hello_world
hello_world_daemon
python_web_app
nodejs_web_app
running_redis_service
running_ssh_service
couchdb_data_volumes

View File

@@ -0,0 +1,236 @@
:title: Running a Node.js app on CentOS
:description: Installing and running a Node.js app on CentOS
:keywords: docker, example, package installation, node, centos
.. _nodejs_web_app:
Node.js Web App
===============
.. include:: example_header.inc
The goal of this example is to show you how you can build your own docker images
from a parent image using a ``Dockerfile`` . We will do that by making a simple
Node.js hello world web application running on CentOS. You can get the full
source code at https://github.com/gasi/docker-node-hello.
Create Node.js app
++++++++++++++++++
First, create a ``package.json`` file that describes your app and its
dependencies:
.. code-block:: json
{
"name": "docker-centos-hello",
"private": true,
"version": "0.0.1",
"description": "Node.js Hello World app on CentOS using docker",
"author": "Daniel Gasienica <daniel@gasienica.ch>",
"dependencies": {
"express": "3.2.4"
}
}
Then, create an ``index.js`` file that defines a web app using the
`Express.js <http://expressjs.com/>`_ framework:
.. code-block:: javascript
var express = require('express');
// Constants
var PORT = 8080;
// App
var app = express();
app.get('/', function (req, res) {
res.send('Hello World\n');
});
app.listen(PORT)
console.log('Running on http://localhost:' + PORT);
In the next steps, well look at how you can run this app inside a CentOS
container using docker. First, youll need to build a docker image of your app.
Creating a ``Dockerfile``
+++++++++++++++++++++++++
Create an empty file called ``Dockerfile``:
.. code-block:: bash
touch Dockerfile
Open the ``Dockerfile`` in your favorite text editor and add the following line
that defines the version of docker the image requires to build
(this example uses docker 0.3.4):
.. code-block:: bash
# DOCKER-VERSION 0.3.4
Next, define the parent image you want to use to build your own image on top of.
Here, well use `CentOS <https://index.docker.io/_/centos/>`_ (tag: ``6.4``)
available on the `docker index`_:
.. code-block:: bash
FROM centos:6.4
Since were building a Node.js app, youll have to install Node.js as well as
npm on your CentOS image. Node.js is required to run your app and npm to install
your apps dependencies defined in ``package.json``.
To install the right package for CentOS, well use the instructions from the
`Node.js wiki`_:
.. code-block:: bash
# Enable EPEL for Node.js
RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
# Install Node.js and npm
RUN yum install -y npm-1.2.17-5.el6
To bundle your apps source code inside the docker image, use the ``ADD``
command:
.. code-block:: bash
# Bundle app source
ADD . /src
Install your app dependencies using npm:
.. code-block:: bash
# Install app dependencies
RUN cd /src; npm install
Your app binds to port ``8080`` so youll use the ``EXPOSE`` command to have it
mapped by the docker daemon:
.. code-block:: bash
EXPOSE 8080
Last but not least, define the command to run your app using ``CMD`` which
defines your runtime, i.e. ``node``, and the path to our app, i.e.
``src/index.js`` (see the step where we added the source to the container):
.. code-block:: bash
CMD ["node", "/src/index.js"]
Your ``Dockerfile`` should now look like this:
.. code-block:: bash
# DOCKER-VERSION 0.3.4
FROM centos:6.4
# Enable EPEL for Node.js
RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
# Install Node.js and npm
RUN yum install -y npm-1.2.17-5.el6
# Bundle app source
ADD . /src
# Install app dependencies
RUN cd /src; npm install
EXPOSE 8080
CMD ["node", "/src/index.js"]
Building your image
+++++++++++++++++++
Go to the directory that has your ``Dockerfile`` and run the following command
to build a docker image. The ``-t`` flag lets you tag your image so its easier
to find later using the ``docker images`` command:
.. code-block:: bash
docker build -t <your username>/centos-node-hello .
Your image will now be listed by docker:
.. code-block:: bash
docker images
> # Example
> REPOSITORY TAG ID CREATED
> centos 6.4 539c0211cd76 8 weeks ago
> gasi/centos-node-hello latest d64d3505b0d2 2 hours ago
Run the image
+++++++++++++
Running your image with ``-d`` runs the container in detached mode, leaving the
container running in the background. Run the image you previously built:
.. code-block:: bash
docker run -d <your username>/centos-node-hello
Print the output of your app:
.. code-block:: bash
# Get container ID
docker ps
# Print app output
docker logs <container id>
> # Example
> Running on http://localhost:8080
Test
++++
To test your app, get the the port of your app that docker mapped:
.. code-block:: bash
docker ps
> # Example
> ID IMAGE COMMAND ... PORTS
> ecce33b30ebf gasi/centos-node-hello:latest node /src/index.js 49160->8080
In the example above, docker mapped the ``8080`` port of the container to
``49160``.
Now you can call your app using ``curl`` (install if needed via:
``sudo apt-get install curl``):
.. code-block:: bash
curl -i localhost:49160
> HTTP/1.1 200 OK
> X-Powered-By: Express
> Content-Type: text/html; charset=utf-8
> Content-Length: 12
> Date: Sun, 02 Jun 2013 03:53:22 GMT
> Connection: keep-alive
>
> Hello World
We hope this tutorial helped you get up and running with Node.js and CentOS on
docker. You can get the full source code at
https://github.com/gasi/docker-node-hello.
Continue to :ref:`running_redis_service`.
.. _Node.js wiki: https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#rhelcentosscientific-linux-6
.. _docker index: https://index.docker.io/

View File

@@ -4,8 +4,8 @@
.. _python_web_app:
Building a python web app
=========================
Python Web App
==============
.. include:: example_header.inc

View File

@@ -4,7 +4,7 @@
.. _running_examples:
Running The Examples
Running the Examples
--------------------
All the examples assume your machine is running the docker daemon. To run the docker daemon in the background, simply type:

View File

@@ -4,8 +4,8 @@
.. _running_redis_service:
Create a redis service
======================
Redis Service
=============
.. include:: example_header.inc
@@ -34,7 +34,7 @@ Snapshot the installation
.. code-block:: bash
docker ps -a # grab the container id (this will be the last one in the list)
docker ps -a # grab the container id (this will be the first one in the list)
docker commit <container_id> <your username>/redis
Run the service
@@ -72,7 +72,7 @@ Connect to the host os with the redis-cli.
docker ps # grab the new container id
docker port <container_id> 6379 # grab the external port
ifconfig # grab the host ip address
ip addr show # grab the host ip address
redis-cli -h <host ipaddress> -p <external port>
redis 192.168.0.1:49153> set docker awesome
OK

View File

@@ -4,8 +4,8 @@
.. _running_ssh_service:
Create an ssh daemon service
============================
SSH Daemon Service
==================
.. include:: example_header.inc
@@ -20,8 +20,7 @@ minutes and not entirely smooth, but gives you a good idea.
<div style="margin-top:10px;">
<iframe width="800" height="400" src="http://ascii.io/a/2637/raw" frameborder="0"></iframe>
</div>
You can also get this sshd container by using
::
@@ -30,3 +29,51 @@ You can also get this sshd container by using
The password is 'screencast'
**Video's Transcription:**
.. code-block:: bash
# Hello! We are going to try and install openssh on a container and run it as a servic
# let's pull base to get a base ubuntu image.
$ docker pull base
# I had it so it was quick
# now let's connect using -i for interactive and with -t for terminal
# we execute /bin/bash to get a prompt.
$ docker run -i -t base /bin/bash
# now let's commit it
# which container was it?
$ docker ps -a |more
$ docker commit a30a3a2f2b130749995f5902f079dc6ad31ea0621fac595128ec59c6da07feea dhrp/sshd
# I gave the name dhrp/sshd for the container
# now we can run it again
$ docker run -d dhrp/sshd /usr/sbin/sshd -D # D for daemon mode
# is it running?
$ docker ps
# yes!
# let's stop it
$ docker stop 0ebf7cec294755399d063f4b1627980d4cbff7d999f0bc82b59c300f8536a562
$ docker ps
# and reconnect, but now open a port to it
$ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D
$ docker port b2b407cf22cf8e7fa3736fa8852713571074536b1d31def3fdfcd9fa4fd8c8c5 22
# it has now given us a port to connect to
# we have to connect using a public ip of our host
$ hostname
# *ifconfig* is deprecated, better use *ip addr show* now
$ ifconfig
$ ssh root@192.168.33.10 -p 49153
# Ah! forgot to set root passwd
$ docker commit b2b407cf22cf8e7fa3736fa8852713571074536b1d31def3fdfcd9fa4fd8c8c5 dhrp/sshd
$ docker ps -a
$ docker run -i -t dhrp/sshd /bin/bash
$ passwd
$ exit
$ docker commit 9e863f0ca0af31c8b951048ba87641d67c382d08d655c2e4879c51410e0fedc1 dhrp/sshd
$ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D
$ docker port a0aaa9558c90cf5c7782648df904a82365ebacce523e4acc085ac1213bfe2206 22
# *ifconfig* is deprecated, better use *ip addr show* now
$ ifconfig
$ ssh root@192.168.33.10 -p 49154
# Thanks for watching, Thatcher thatcher@dotcloud.com

View File

@@ -19,7 +19,8 @@ Most frequently asked questions.
3. **Does Docker run on Mac OS X or Windows?**
Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a virtual machine on your box, and get the best of both worlds. Check out the MacOSX_ and Windows_ installation guides.
Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a
virtual machine on your box, and get the best of both worlds. Check out the :ref:`install_using_vagrant` and :ref:`windows` installation guides.
4. **How do containers compare to virtual machines?**
@@ -34,15 +35,16 @@ Most frequently asked questions.
You can find more answers on:
* `IRC: docker on freenode`_
* `Docker club mailinglist`_
* `IRC, docker on freenode`_
* `Github`_
* `Ask questions on Stackoverflow`_
* `Join the conversation on Twitter`_
.. _Windows: ../installation/windows/
.. _MacOSX: ../installation/vagrant/
.. _Docker club mailinglist: https://groups.google.com/d/forum/docker-club
.. _the repo: http://www.github.com/dotcloud/docker
.. _IRC\: docker on freenode: irc://chat.freenode.net#docker
.. _IRC, docker on freenode: irc://chat.freenode.net#docker
.. _Github: http://www.github.com/dotcloud/docker
.. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker
.. _Join the conversation on Twitter: http://twitter.com/getdocker

View File

@@ -7,8 +7,8 @@
Introduction
============
Docker - The Linux container runtime
------------------------------------
Docker -- The Linux container runtime
-------------------------------------
Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.

View File

@@ -1,27 +0,0 @@
:title: Index Environment Variable
:description: Setting this environment variable on the docker server will change the URL docker index.
:keywords: docker, index environment variable, documentation
=================================
Docker Index Environment Variable
=================================
Variable
--------
.. code-block:: sh
DOCKER_INDEX_URL
Setting this environment variable on the docker server will change the URL docker index.
This address is used in commands such as ``docker login``, ``docker push`` and ``docker pull``.
The docker daemon doesn't need to be restarted for this parameter to take effect.
Example
-------
.. code-block:: sh
docker -d &
export DOCKER_INDEX_URL="https://index.docker.io"

View File

@@ -30,8 +30,7 @@ Dependencies:
* 3.8 Kernel (read more about :ref:`kernel`)
* AUFS filesystem support
* lxc
* bsdtar
* xz-utils
Get the docker binary:
----------------------

View File

@@ -100,7 +100,7 @@ Memory and Swap Accounting on Debian/Ubuntu
If you use Debian or Ubuntu kernels, and want to enable memory and swap
accounting, you must add the following command-line parameters to your kernel::
cgroup_enable=memory swapaccount
cgroup_enable=memory swapaccount=1
On Debian or Ubuntu systems, if you use the default GRUB bootloader, you can
add those parameters by editing ``/etc/default/grub`` and extending
@@ -110,6 +110,6 @@ add those parameters by editing ``/etc/default/grub`` and extending
And replace it by the following one::
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount"
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
Then run ``update-grub``, and reboot.

View File

@@ -92,6 +92,16 @@ have AUFS filesystem support enabled, so we need to install it.
sudo apt-get update
sudo apt-get install linux-image-extra-`uname -r`
**add-apt-repository support**
Some installations of Ubuntu 13.04 require ``software-properties-common`` to be
installed before being able to use add-apt-repository.
.. code-block:: bash
sudo apt-get install software-properties-common
Installation
------------

View File

@@ -2,6 +2,7 @@
:description: Docker's tutorial to run docker on Windows
:keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
.. _windows:
Using Vagrant (Windows)
=======================

View File

@@ -3,8 +3,8 @@
:keywords: Examples, Usage, basic commands, docker, documentation, examples
The basics
=============
The Basics
==========
Starting Docker
---------------
@@ -33,11 +33,20 @@ Running an interactive shell
# allocate a tty, attach stdin and stdout
docker run -i -t base /bin/bash
Bind Docker to another host/port
--------------------------------
Bind Docker to another host/port or a unix socket
-------------------------------------------------
If you want Docker to listen to another port and bind to another ip
use -host and -port on both deamon and client
With -H it is possible to make the Docker daemon to listen on a specific ip and port. By default, it will listen on 127.0.0.1:4243 to allow only local connections but you can set it to 0.0.0.0:4243 or a specific host ip to give access to everybody.
Similarly, the Docker client can use -H to connect to a custom port.
-H accepts host and port assignment in the following format: tcp://[host][:port] or unix://path
For example:
* tcp://host -> tcp connection on host:4243
* tcp://host:port -> tcp connection on host:port
* tcp://:port -> tcp connection on 127.0.0.1:port
* unix://path/to/socket -> unix socket located at path/to/socket
.. code-block:: bash
@@ -46,6 +55,17 @@ use -host and -port on both deamon and client
# Download a base image
docker -H :5555 pull base
You can use multiple -H, for example, if you want to listen
on both tcp and a unix socket
.. code-block:: bash
# Run docker in daemon mode
sudo <path to>/docker -H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock
# Download a base image
docker pull base
# OR
docker -H unix:///var/run/docker.sock pull base
Starting a long-running worker process
--------------------------------------
@@ -82,7 +102,8 @@ Expose a service on a TCP port
# Connect to the public port via the host's public address
# Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work.
IP=$(ifconfig eth0 | perl -n -e 'if (m/inet addr:([\d\.]+)/g) { print $1 }')
# Replace *eth0* according to your local interface name.
IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }')
echo hello world | nc $IP $PORT
# Verify that the network connection worked

View File

@@ -15,10 +15,18 @@ steps and commit them along the way, giving you a final image.
1. Usage
========
To use Docker Builder, assemble the steps into a text file (commonly referred to
as a Dockerfile) and supply this to `docker build` on STDIN, like so:
To build an image from a source repository, create a description file called `Dockerfile`
at the root of your repository. This file will describe the steps to assemble
the image.
``docker build < Dockerfile``
Then call `docker build` with the path of your source repository as argument:
``docker build .``
You can specify a repository and tag at which to save the new image if the
build succeeds:
``docker build -t shykes/myapp .``
Docker will run your steps one-by-one, committing the result if necessary,
before finally outputting the ID of your new image.
@@ -113,26 +121,37 @@ functionally equivalent to prefixing the command with `<key>=<value>`
.. note::
The environment variables will persist when a container is run from the resulting image.
2.7 INSERT
----------
``INSERT <file url> <path>``
The `INSERT` instruction will download the file from the given url to the given
path within the image. It is similar to `RUN curl -o <path> <url>`, assuming
curl was installed within the image.
.. note::
The path must include the file name.
2.8 ADD
2.7 ADD
-------
``ADD <src> <dest>``
The `ADD` instruction will insert the files from the `<src>` path of the context into `<dest>` path
of the container.
The context must be set in order to use this instruction. (see examples)
The `ADD` instruction will copy new files from <src> and add them to the container's filesystem at path `<dest>`.
`<src>` must be the path to a file or directory relative to the source directory being built (also called the
context of the build) or a remote file URL.
`<dest>` is the path at which the source will be copied in the destination container.
The copy obeys the following rules:
If `<src>` is a directory, the entire directory is copied, including filesystem metadata.
If `<src>` is a tar archive in a recognized compression format (identity, gzip, bzip2 or xz), it
is unpacked as a directory.
When a directory is copied or unpacked, it has the same behavior as 'tar -x': the result is the union of
a) whatever existed at the destination path and b) the contents of the source tree, with conflicts resolved
in favor of b on a file-by-file basis.
If `<src>` is any other kind of file, it is copied individually along with its metadata. In this case,
if `<dst>` ends with a trailing slash '/', it will be considered a directory and the contents of `<src>`
will be written at `<dst>/base(<src>)`.
If `<dst>` does not end with a trailing slash, it will be considered a regular file and the contents
of `<src>` will be written at `<dst>`.
If `<dest>` doesn't exist, it is created along with all missing directories in its path. All new
files and directories are created with mode 0700, uid and gid 0.
3. Dockerfile Examples
======================
@@ -151,7 +170,6 @@ The context must be set in order to use this instruction. (see examples)
RUN apt-get update
RUN apt-get install -y inotify-tools nginx apache2 openssh-server
INSERT https://raw.github.com/creack/docker-vps/master/nginx-wrapper.sh /usr/sbin/nginx-wrapper
.. code-block:: bash

View File

@@ -14,6 +14,7 @@ Contents:
basics
workingwithrepository
port_redirection
builder
puppet

View File

@@ -0,0 +1,25 @@
:title: Port redirection
:description: usage about port redirection
:keywords: Usage, basic port, docker, documentation, examples
Port redirection
================
Docker can redirect public tcp ports to your container, so it can be reached over the network.
Port redirection is done on ``docker run`` using the -p flag.
A port redirect is specified as PUBLIC:PRIVATE, where tcp port PUBLIC will be redirected to
tcp port PRIVATE. As a special case, the public port can be omitted, in which case a random
public port will be allocated.
.. code-block:: bash
# A random PUBLIC port is redirected to PRIVATE port 80 on the container
docker run -p 80 <image> <cmd>
# PUBLIC port 80 is redirected to PRIVATE port 80
docker run -p 80:80 <image> <cmd>
Default port redirects can be built into a container with the EXPOSE build command.

View File

@@ -4,8 +4,8 @@
.. _working_with_the_repository:
Working with the repository
============================
Working with the Repository
===========================
Top-level repositories and user repositories
@@ -14,9 +14,9 @@ Top-level repositories and user repositories
Generally, there are two types of repositories: Top-level repositories which are controlled by the people behind
Docker, and user repositories.
* Top-level repositories can easily be recognized by not having a / (slash) in their name. These repositories can
* Top-level repositories can easily be recognized by not having a ``/`` (slash) in their name. These repositories can
generally be trusted.
* User repositories always come in the form of <username>/<repo_name>. This is what your published images will look like.
* User repositories always come in the form of ``<username>/<repo_name>``. This is what your published images will look like.
* User images are not checked, it is therefore up to you whether or not you trust the creator of this image.
@@ -77,3 +77,28 @@ Now you can commit this image to the repository
# for example docker push dhrp/kickassapp
docker push <image-name>
Changing the server to connect to
----------------------------------
When you are running your own index and/or registry, You can change the server the docker client will connect to.
Variable
^^^^^^^^
.. code-block:: sh
DOCKER_INDEX_URL
Setting this environment variable on the docker server will change the URL docker index.
This address is used in commands such as ``docker login``, ``docker push`` and ``docker pull``.
The docker daemon doesn't need to be restarted for this parameter to take effect.
Example
^^^^^^^
.. code-block:: sh
docker -d &
export DOCKER_INDEX_URL="https://index.docker.io"

View File

@@ -40,6 +40,8 @@
{%- set script_files = script_files + ['_static/js/docs.js'] %}
<link rel="canonical" href="http://docs.docker.io/en/latest/{{ pagename }}/">
{%- for cssfile in css_files %}
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
{%- endfor %}
@@ -48,9 +50,8 @@
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
{%- endfor %}
{%- if favicon %}
<link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
{%- endif %}
<link rel="shortcut icon" href="{{ pathto('_static/favicon.png', 1) }}"/>
{%- block extrahead %}{% endblock %}
@@ -64,14 +65,15 @@
<div style="float: right" class="pull-right">
<ul class="nav">
<li><a href="http://www.docker.io/">Introduction</a></li>
<li><a href="http://www.docker.io/gettingstarted/">Getting started</a></li>
<li class="active"><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-introduction"><a href="http://www.docker.io/">Introduction</a></li>
<li id="nav-gettingstarted"><a href="http://www.docker.io/gettingstarted/">Getting started</a></li>
<li id="nav-documentation" class="active"><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-blog"><a href="http://blog.docker.io/">Blog</a></li>
</ul>
<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
<!--<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">-->
<!--<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>-->
<!--<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>-->
<!--</div>-->
</div>
<div style="margin-left: -12px; float: left;">
@@ -86,8 +88,13 @@
<div class="container">
<div class="row">
<div class="span12 titlebar"><h1 class="pageheader">DOCUMENTATION</h1>
<div class="span12 titlebar">
<!--<span class="pull-right" style="margin-left: 20px; font-size: 20px">{{version}}</span>-->
<div class="pull-right" id="fork-us" style="margin-top: 16px; margin-right: 16px;">
<a href="http://github.com/dotcloud/docker/"><img src="{{ pathto('_static/img/fork-us.png', 1) }}"> Fork us on Github</a>
</div>
<h1 class="pageheader">DOCUMENTATION</h1>
</div>
</div>
@@ -98,11 +105,8 @@
<!-- Docs nav
================================================== -->
<div class="row" style="position: relative">
<div class="span3" style="height:100%;" >
</div>
<div class="span3 sidebar bs-docs-sidebar" style="position: absolute">
<div class="span3 sidebar bs-docs-sidebar">
{{ toctree(collapse=False, maxdepth=3) }}
</div>
@@ -123,8 +127,14 @@
<div class="row">
<div class="span12 footer">
<div class="tbox textright forceleftmargin social links pull-right">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
{# {%- if show_source and has_source and sourcename %}#}
{# ·#}
{# <a href="{{ pathto('_sources/' + sourcename, true)|e }}"#}
@@ -157,7 +167,7 @@
<!-- script which should be loaded after everything else -->
<script type="text/javascript">
// Function to make the sticky header possible
var shiftWindow = function() {
scrollBy(0, -70);
console.log("window shifted")

12
docs/theme/docker/redirect_home.html vendored Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Page Moved</title>
<meta http-equiv="refresh" content="0; url=http://docks.docker.io/en/latest/">
</head>
<body>
This page has moved. Perhaps you should visit the <a href="http://docs.docker.io/" title="documentation homepage">Documentation Homepage</a>
</body>
</html>

View File

@@ -168,10 +168,13 @@ section.header {
.sidebar {
font-weight: normal;
float: left;
min-height: 475px;
/* min-height: 475px;*/
background: #ececec;
border-left: 1px solid #bbbbbb;
border-right: 1px solid #cccccc;
/* border-left: 1px solid #bbbbbb;*/
/* border-right: 1px solid #cccccc;*/
position: relative;
}
.sidebar ul {
@@ -285,6 +288,40 @@ section.header {
.social .github {
background-position: -59px 2px;
}
#fork-us {
/*font-family: 'Maven Pro';*/
/*font-weight: bold;*/
font-size: 12px;
/*text-transform: uppercase;*/
display: block;
padding: 0px 1em;
height: 28px;
line-height: 28px;
background-color: #43484c;
filter: progid:dximagetransform.microsoft.gradient(gradientType=0, startColorstr='#FFFF6E56', endColorstr='#FFED4F35');
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #747474), color-stop(100%, #43484c));
background-image: -webkit-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: -moz-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: -o-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: linear-gradient(top, #747474 0%, #43484c 100%);
border: 1px solid #43484c;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-ms-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
-moz-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
margin: 8px;
}
#fork-us a {
color: #faf2ee;
text-shadow: rgba(0, 0, 0, 0.3) 0px 1px 0px;
}
/* =======================
Media size overrides
======================= */
@@ -323,12 +360,16 @@ section.header {
#global {
/* TODO: Fix this to be relative to the navigation size */
padding-top: 600px;
}
#fork-us {
display: none;
}
}
/* Landscape phones and down */
@media (max-width: 480px) {
#nav-gettingstarted {
display: none;
}
}
/* Misc fixes */
table th {

View File

@@ -226,20 +226,21 @@ section.header {
}
.sidebar {
// font-family: "Maven Pro";
font-weight: normal;
// margin-top: 38px;
float: left;
// width: 220px;
/* min-height: 475px;*/
// margin-bottom: 28px;
// padding-bottom: 120px;
background: #ececec;
/* border-left: 1px solid #bbbbbb;*/
/* border-right: 1px solid #cccccc;*/
position: relative;
.sidebar {
// font-family: "Maven Pro";
font-weight: normal;
// margin-top: 38px;
float: left;
// width: 220px;
min-height: 475px;
// margin-bottom: 28px;
// padding-bottom: 120px;
background: #ececec;
border-left: 1px solid #bbbbbb;
border-right: 1px solid #cccccc;
position: relative;
ul {
padding: 0px;
@@ -391,6 +392,38 @@ section.header {
}
#fork-us {
/*font-family: 'Maven Pro';*/
/*font-weight: bold;*/
font-size: 12px;
/*text-transform: uppercase;*/
display: block;
padding: 0px 1em;
height: 28px;
line-height: 28px;
background-color: #43484c;
filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#FFFF6E56', endColorstr='#FFED4F35');
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #747474), color-stop(100%, #43484c));
background-image: -webkit-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: -moz-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: -o-linear-gradient(top, #747474 0%, #43484c 100%);
background-image: linear-gradient(top, #747474 0%, #43484c 100%);
border: 1px solid #43484c;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-ms-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
-moz-box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
box-shadow: inset rgba(255, 255, 255, 0.17) 0 1px 1px;
margin: 8px;
a {
color: #faf2ee;
text-shadow: rgba(0, 0, 0, 0.3) 0px 1px 0px;
}
}
/* =======================
Media size overrides
======================= */
@@ -439,16 +472,19 @@ section.header {
}
#global {
/* TODO: Fix this to be relative to the navigation size */
padding-top: 600px;
// padding-top: 600px;
}
#fork-us {
display: none;
}
}
/* Landscape phones and down */
@media (max-width: 480px) {
#nav-gettingstarted {
display: none;
}
}
/* Misc fixes */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
docs/theme/docker/static/favicon.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

View File

@@ -34,15 +34,11 @@
<div style="float: right" class="pull-right">
<ul class="nav">
<li><a href="../">Introduction</a></li>
<li class="active"><a href="">Getting started</a></li>
<li class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-introduction"><a href="../">Introduction</a></li>
<li id="nav-gettingstarted" class="active"><a href="">Getting started</a></li>
<li id="nav-documentation" class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-blog"><a href="http://blog.docker.io/">Blog</a></li>
</ul>
<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
</div>
<div style="margin-left: -12px; float: left;">
@@ -55,14 +51,22 @@
<div class="container">
<div class="row">
<div class="span12 titlebar"><h1 class="pageheader">GETTING STARTED</h1>
<div class="span12 titlebar">
<div class="pull-right" id="fork-us" style="margin-top: 16px; margin-right: 16px;">
<a href="http://github.com/dotcloud/docker/"><img src="../static/img/fork-us.png"> Fork us on Github</a>
</div>
<h1 class="pageheader"> GETTING STARTED</h1>
</div>
</div>
</div>
<div class="container">
<div class="alert alert-info">
<div class="alert alert-info" style="margin-bottom: 0;">
<strong>Docker is still under heavy development.</strong> It should not yet be used in production. Check <a href="http://github.com/dotcloud/docker">the repo</a> for recent progress.
</div>
<div class="row">
@@ -133,13 +137,13 @@
</section>
<section class="contentblock">
<h2>More resources</h2>
<ul>
<li><a href="irc://chat.freenode.net#docker">IRC: docker on freenode</a></li>
<li><a href="http://www.github.com/dotcloud/docker">Github</a></li>
<li><a href="http://stackoverflow.com/tags/docker/">Ask questions on Stackoverflow</a></li>
<li><a href="http://twitter.com/getdocker/">Join the conversation on Twitter</a></li>
</ul>
<h2>Questions? Want to get in touch?</h2>
<p>There are several ways to get in touch:</p>
<p><strong>Join the discussion on IRC.</strong> We can be found in the <a href="irc://chat.freenode.net#docker">#docker</a> channel on chat.freenode.net</p>
<p><strong>Discussions</strong> happen on our google group: <a href="https://groups.google.com/d/forum/docker-club">docker-club at googlegroups.com</a></p>
<p>All our <strong>development and decisions</strong> are made out in the open on Github <a href="http://www.github.com/dotcloud/docker">github.com/dotcloud/docker</a></p>
<p><strong>Get help on using Docker</strong> by asking on <a href="http://stackoverflow.com/tags/docker/">Stackoverflow</a></p>
<p>And of course, <strong>tweet</strong> your tweets to <a href="http://twitter.com/getdocker/">twitter.com/getdocker</a></p>
</section>
@@ -172,7 +176,10 @@
<footer id="footer" class="footer">
<div class="row">
<div class="span12 social">
<div class="tbox textright forceleftmargin social links pull-right">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
</div>

View File

@@ -44,9 +44,18 @@
.debug {
border: 1px red dotted;
}
.twitterblock {
min-height: 75px;
}
.twitterblock img {
float: left;
margin-right: 10px;
}
</style>
</head>
@@ -56,17 +65,18 @@
<div class="navbar-dotcloud">
<div class="container" style="text-align: center;">
<div class="pull-left" id="fork-us" style="margin-top: 16px;">
<a href="http://github.com/dotcloud/docker/"><img src="static/img/fork-us.png" alt="fork-icon"> Fork us on Github</a>
</div>
<div class="pull-right" >
<ul class="nav">
<li class="active"><a href="/">Introduction</a></li>
<li ><a href="gettingstarted">Getting started</a></li>
<li class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-introduction" class="active"><a href="/">Introduction</a></li>
<li id="nav-gettingstarted"><a href="gettingstarted">Getting started</a></li>
<li id="nav-documentation" class=""><a href="http://docs.docker.io/en/latest/">Documentation</a></li>
<li id="nav-blog"><a href="http://blog.docker.io/">Blog</a></li>
</ul>
<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
</div>
</div>
</div>
@@ -81,7 +91,7 @@
<div class="span5" style="margin-bottom: 15px;">
<div style="text-align: center;" >
<img src="static/img/docker_letters_500px.png">
<img src="static/img/docker_letters_500px.png" alt="docker letters">
<h2>The Linux container engine</h2>
</div>
@@ -130,7 +140,7 @@
<section class="contentblock">
<div class="container">
<div class="span2" style="margin-left: 0" >
<a href="http://dotcloud.theresumator.com/apply/mWjkD4/Software-Engineer.html" title="Job description"><img src="static/img/hiring_graphic.png" width="140px" style="margin-top: 25px"></a>
<a href="http://dotcloud.theresumator.com/apply/mWjkD4/Software-Engineer.html" title="Job description"><img src="static/img/hiring_graphic.png" alt="we're hiring" width="140" style="margin-top: 25px"></a>
</div>
<div class="span4" style="margin-left: 0">
<h4>Do you think it is cool to hack on docker? Join us!</h4>
@@ -156,7 +166,7 @@
</div>
</a>
&nbsp;
<input type="button" class="searchbutton" type="submit" value="Search images"
<input type="button" class="searchbutton" value="Search images"
onClick="window.open('https://index.docker.io')" />
</section>
@@ -184,32 +194,19 @@
</div>
<style>
.twitterblock {
min-height: 75px;
}
.twitterblock img {
float: left;
margin-right: 10px;
}
</style>
<div class="container">
<div class="row">
<div class="span6">
<section class="contentblock twitterblock">
<img src="https://si0.twimg.com/profile_images/2707460527/252a64411a339184ff375a96fb68dcb0_bigger.png">
<em>Mitchell Hashimoto@mitchellh:</em> Docker launched today. It is incredible. Theyre also working RIGHT NOW on a Vagrant provider. LXC is COMING!!
<em>Mitchell Hashimoto @mitchellh:</em> Docker launched today. It is incredible. Theyre also working RIGHT NOW on a Vagrant provider. LXC is COMING!!
</section>
</div>
<div class="span6">
<section class="contentblock twitterblock">
<img src="https://si0.twimg.com/profile_images/1108290260/Adam_Jacob-114x150_original_bigger.jpg">
<em>Adam Jacob@adamhjk:</em> Docker is clearly the right idea. @solomonstre absolutely killed it. Containerized app deployment is the future, I think.
<em>Adam Jacob @adamhjk:</em> Docker is clearly the right idea. @solomonstre absolutely killed it. Containerized app deployment is the future, I think.
</section>
</div>
</div>
@@ -217,13 +214,13 @@
<div class="span6">
<section class="contentblock twitterblock">
<img src="https://si0.twimg.com/profile_images/14872832/twitter_pic_bigger.jpg">
<em>Matt Townsend@mtownsend:</em> I have a serious code crush on docker.io - it's Lego for PaaS. Motherfucking awesome Lego.
<em>Matt Townsend @mtownsend:</em> I have a serious code crush on docker.io - it's Lego for PaaS. Motherfucking awesome Lego.
</section>
</div>
<div class="span6">
<section class="contentblock twitterblock">
<img src="https://si0.twimg.com/profile_images/1312352395/rupert-259x300_bigger.jpg">
<em>Rob Harrop@robertharrop:</em> Impressed by @getdocker - it's all kinds of magic. Serious rethink of AWS architecture happening @skillsmatter.
<em>Rob Harrop @robertharrop:</em> Impressed by @getdocker - it's all kinds of magic. Serious rethink of AWS architecture happening @skillsmatter.
</section>
</div>
</div>
@@ -270,7 +267,7 @@
<li>Filesystem isolation: each process container runs in a completely separate root filesystem.</li>
<li>Resource isolation: system resources like cpu and memory can be allocated differently to each process container, using cgroups.</li>
<li>Network isolation: each process container runs in its own network namespace, with a virtual interface and IP address of its own.</li>
<li>Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremeley fast, memory-cheap and disk-cheap.</li>
<li>Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremely fast, memory-cheap and disk-cheap.</li>
<li>Logging: the standard streams (stdout/stderr/stdin) of each process container is collected and logged for real-time or batch retrieval.</li>
<li>Change management: changes to a container's filesystem can be committed into a new image and re-used to create more containers. No templating or manual configuration required.</li>
<li>Interactive shell: docker can allocate a pseudo-tty and attach to the standard input of any container, for example to run a throwaway interactive shell.</li>
@@ -317,7 +314,10 @@
<footer id="footer" class="footer">
<div class="row">
<div class="span12">
<div class="tbox textright forceleftmargin social links pull-right">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
</div>

View File

@@ -1,10 +0,0 @@
package docker
import (
"fmt"
"github.com/dotcloud/docker/utils"
)
func getKernelVersion() (*utils.KernelVersionInfo, error) {
return nil, fmt.Errorf("Kernel version detection is not available on darwin")
}

View File

@@ -1,71 +0,0 @@
package docker
import (
"bytes"
"github.com/dotcloud/docker/utils"
"strconv"
"strings"
"syscall"
)
// FIXME: Move this to utils package
func getKernelVersion() (*utils.KernelVersionInfo, error) {
var (
uts syscall.Utsname
flavor string
kernel, major, minor int
err error
)
if err := syscall.Uname(&uts); err != nil {
return nil, err
}
release := make([]byte, len(uts.Release))
i := 0
for _, c := range uts.Release {
release[i] = byte(c)
i++
}
// Remove the \x00 from the release for Atoi to parse correctly
release = release[:bytes.IndexByte(release, 0)]
tmp := strings.SplitN(string(release), "-", 2)
tmp2 := strings.SplitN(tmp[0], ".", 3)
if len(tmp2) > 0 {
kernel, err = strconv.Atoi(tmp2[0])
if err != nil {
return nil, err
}
}
if len(tmp2) > 1 {
major, err = strconv.Atoi(tmp2[1])
if err != nil {
return nil, err
}
}
if len(tmp2) > 2 {
minor, err = strconv.Atoi(tmp2[2])
if err != nil {
return nil, err
}
}
if len(tmp) == 2 {
flavor = tmp[1]
} else {
flavor = ""
}
return &utils.KernelVersionInfo{
Kernel: kernel,
Major: major,
Minor: minor,
Flavor: flavor,
}, nil
}

View File

@@ -86,14 +86,23 @@ func (graph *Graph) Get(name string) (*Image, error) {
if err != nil {
return nil, err
}
if img.Id != id {
return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.Id)
if img.ID != id {
return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
}
img.graph = graph
if img.Size == 0 {
root, err := img.root()
if err != nil {
return nil, err
}
if err := StoreSize(img, root); err != nil {
return nil, err
}
}
graph.lockSumMap.Lock()
defer graph.lockSumMap.Unlock()
if _, exists := graph.checksumLock[img.Id]; !exists {
graph.checksumLock[img.Id] = &sync.Mutex{}
if _, exists := graph.checksumLock[img.ID]; !exists {
graph.checksumLock[img.ID] = &sync.Mutex{}
}
return img, nil
}
@@ -101,16 +110,17 @@ 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, config *Config) (*Image, error) {
img := &Image{
Id: GenerateId(),
ID: GenerateID(),
Comment: comment,
Created: time.Now(),
DockerVersion: VERSION,
Author: author,
Config: config,
Architecture: "x86_64",
}
if container != nil {
img.Parent = container.Image
img.Container = container.Id
img.Container = container.ID
img.ContainerConfig = *container.Config
}
if err := graph.Register(layerData, layerData != nil, img); err != nil {
@@ -123,12 +133,12 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
// Register imports a pre-existing image into the graph.
// FIXME: pass img as first argument
func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
if err := ValidateId(img.Id); err != nil {
if err := ValidateID(img.ID); err != nil {
return err
}
// (This is a convenience to save time. Race conditions are taken care of by os.Rename)
if graph.Exists(img.Id) {
return fmt.Errorf("Image %s already exists", img.Id)
if graph.Exists(img.ID) {
return fmt.Errorf("Image %s already exists", img.ID)
}
tmp, err := graph.Mktemp("")
defer os.RemoveAll(tmp)
@@ -139,12 +149,12 @@ func (graph *Graph) Register(layerData Archive, store bool, img *Image) error {
return err
}
// Commit
if err := os.Rename(tmp, graph.imageRoot(img.Id)); err != nil {
if err := os.Rename(tmp, graph.imageRoot(img.ID)); err != nil {
return err
}
img.graph = graph
graph.idIndex.Add(img.Id)
graph.checksumLock[img.Id] = &sync.Mutex{}
graph.idIndex.Add(img.ID)
graph.checksumLock[img.ID] = &sync.Mutex{}
return nil
}
@@ -165,13 +175,14 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output
if err != nil {
return nil, err
}
return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)", false), tmp.Root)
sf := utils.NewStreamFormatter(false)
return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, sf.FormatProgress("Buffering to disk", "%v/%v (%v)"), sf), tmp.Root)
}
// Mktemp creates a temporary sub-directory inside the graph's filesystem.
func (graph *Graph) Mktemp(id string) (string, error) {
if id == "" {
id = GenerateId()
id = GenerateID()
}
tmp, err := graph.tmp()
if err != nil {
@@ -228,7 +239,7 @@ func (graph *Graph) Map() (map[string]*Image, error) {
}
images := make(map[string]*Image, len(all))
for _, image := range all {
images[image.Id] = image
images[image.ID] = image
}
return images, nil
}
@@ -271,10 +282,10 @@ func (graph *Graph) ByParent() (map[string][]*Image, error) {
if err != nil {
return
}
if children, exists := byParent[parent.Id]; exists {
byParent[parent.Id] = []*Image{image}
if children, exists := byParent[parent.ID]; exists {
byParent[parent.ID] = []*Image{image}
} else {
byParent[parent.Id] = append(children, image)
byParent[parent.ID] = append(children, image)
}
})
return byParent, err
@@ -291,8 +302,8 @@ func (graph *Graph) Heads() (map[string]*Image, error) {
err = graph.WalkAll(func(image *Image) {
// If it's not in the byParent lookup table, then
// it's not a parent -> so it's a head!
if _, exists := byParent[image.Id]; !exists {
heads[image.Id] = image
if _, exists := byParent[image.ID]; !exists {
heads[image.ID] = image
}
})
return heads, err
@@ -315,11 +326,11 @@ func (graph *Graph) getStoredChecksums() (map[string]string, error) {
}
func (graph *Graph) storeChecksums(checksums map[string]string) error {
checksumJson, err := json.Marshal(checksums)
checksumJSON, err := json.Marshal(checksums)
if err != nil {
return err
}
if err := ioutil.WriteFile(path.Join(graph.Root, "checksums"), checksumJson, 0600); err != nil {
if err := ioutil.WriteFile(path.Join(graph.Root, "checksums"), checksumJSON, 0600); err != nil {
return err
}
return nil

View File

@@ -34,14 +34,14 @@ func TestInterruptedRegister(t *testing.T) {
defer os.RemoveAll(graph.Root)
badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
image := &Image{
Id: GenerateId(),
ID: GenerateID(),
Comment: "testing",
Created: time.Now(),
}
go graph.Register(badArchive, false, image)
time.Sleep(200 * time.Millisecond)
w.CloseWithError(errors.New("But I'm not a tarball!")) // (Nobody's perfect, darling)
if _, err := graph.Get(image.Id); err == nil {
if _, err := graph.Get(image.ID); err == nil {
t.Fatal("Image should not exist after Register is interrupted")
}
// Registering the same image again should succeed if the first register was interrupted
@@ -67,7 +67,7 @@ func TestGraphCreate(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if err := ValidateId(image.Id); err != nil {
if err := ValidateID(image.ID); err != nil {
t.Fatal(err)
}
if image.Comment != "Testing" {
@@ -91,7 +91,7 @@ func TestRegister(t *testing.T) {
t.Fatal(err)
}
image := &Image{
Id: GenerateId(),
ID: GenerateID(),
Comment: "testing",
Created: time.Now(),
}
@@ -104,11 +104,11 @@ func TestRegister(t *testing.T) {
} else if l := len(images); l != 1 {
t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
}
if resultImg, err := graph.Get(image.Id); err != nil {
if resultImg, err := graph.Get(image.ID); err != nil {
t.Fatal(err)
} else {
if resultImg.Id != image.Id {
t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.Id, resultImg.Id)
if resultImg.ID != image.ID {
t.Fatalf("Wrong image ID. Should be '%s', not '%s'", image.ID, resultImg.ID)
}
if resultImg.Comment != image.Comment {
t.Fatalf("Wrong image comment. Should be '%s', not '%s'", image.Comment, resultImg.Comment)
@@ -156,7 +156,7 @@ func TestDeletePrefix(t *testing.T) {
graph := tempGraph(t)
defer os.RemoveAll(graph.Root)
img := createTestImage(graph, t)
if err := graph.Delete(utils.TruncateId(img.Id)); err != nil {
if err := graph.Delete(utils.TruncateID(img.ID)); err != nil {
t.Fatal(err)
}
assertNImages(graph, t, 0)
@@ -187,21 +187,29 @@ func TestDelete(t *testing.T) {
t.Fatal(err)
}
assertNImages(graph, t, 1)
if err := graph.Delete(img.Id); err != nil {
if err := graph.Delete(img.ID); err != nil {
t.Fatal(err)
}
assertNImages(graph, t, 0)
archive, err = fakeTar()
if err != nil {
t.Fatal(err)
}
// Test 2 create (same name) / 1 delete
img1, err := graph.Create(archive, nil, "Testing", "", nil)
if err != nil {
t.Fatal(err)
}
archive, err = fakeTar()
if err != nil {
t.Fatal(err)
}
if _, err = graph.Create(archive, nil, "Testing", "", nil); err != nil {
t.Fatal(err)
}
assertNImages(graph, t, 2)
if err := graph.Delete(img1.Id); err != nil {
if err := graph.Delete(img1.ID); err != nil {
t.Fatal(err)
}
assertNImages(graph, t, 1)
@@ -212,11 +220,15 @@ func TestDelete(t *testing.T) {
}
assertNImages(graph, t, 1)
archive, err = fakeTar()
if err != nil {
t.Fatal(err)
}
// Test delete twice (pull -> rm -> pull -> rm)
if err := graph.Register(archive, false, img1); err != nil {
t.Fatal(err)
}
if err := graph.Delete(img1.Id); err != nil {
if err := graph.Delete(img1.ID); err != nil {
t.Fatal(err)
}
assertNImages(graph, t, 1)

19
hack/PRINCIPLES.md Normal file
View File

@@ -0,0 +1,19 @@
# Docker principles
In the design and development of Docker we try to follow these principles:
(Work in progress)
* Don't try to replace every tool. Instead, be an ingredient to improve them.
* Less code is better.
* Less components is better. Do you really need to add one more class?
* 50 lines of straightforward, readable code is better than 10 lines of magic that nobody can understand.
* Don't do later what you can do now. "//FIXME: refactor" is not acceptable in new code.
* When hesitating between 2 options, choose the one that is easier to reverse.
* No is temporary, Yes is forever. If you're not sure about a new feature, say no. You can change your mind later.
* Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable.
* The less moving parts in a container, the better.
* Don't merge it unless you document it.
* Don't document it unless you can keep it up-to-date.
* Don't merge it unless you test it!
* Everyone's problem is slightly different. Focus on the part that is the same for everyone, and solve that.

105
hack/ROADMAP.md Normal file
View File

@@ -0,0 +1,105 @@
# Docker: what's next?
This document is a high-level overview of where we want to take Docker next.
It is a curated selection of planned improvements which are either important, difficult, or both.
For a more complete view of planned and requested improvements, see [the Github issues](https://github.com/dotcloud/docker/issues).
Tu suggest changes to the roadmap, including additions, please write the change as if it were already in effect, and make a pull request.
Broader kernel support
----------------------
Our goal is to make Docker run everywhere, but currently Docker requires [Linux version 3.8 or higher with lxc and aufs support](http://docs.docker.io/en/latest/installation/kernel.html). If you're deploying new machines for the purpose of running Docker, this is a fairly easy requirement to meet.
However, if you're adding Docker to an existing deployment, you may not have the flexibility to update and patch the kernel.
Expanding Docker's kernel support is a priority. This includes running on older kernel versions,
but also on kernels with no AUFS support, or with incomplete lxc capabilities.
Cross-architecture support
--------------------------
Our goal is to make Docker run everywhere. However currently Docker only runs on x86_64 systems.
We plan on expanding architecture support, so that Docker containers can be created and used on more architectures.
Even more integrations
----------------------
We want Docker to be the secret ingredient that makes your existing tools more awesome.
Thanks to this philosophy, Docker has already been integrated with
[Puppet](http://forge.puppetlabs.com/garethr/docker), [Chef](http://www.opscode.com/chef),
[Openstack Nova](https://github.com/dotcloud/openstack-docker), [Jenkins](https://github.com/georgebashi/jenkins-docker-plugin),
[DotCloud sandbox](http://github.com/dotcloud/sandbox), [Pallet](https://github.com/pallet/pallet-docker),
[Strider CI](http://blog.frozenridge.co/next-generation-continuous-integration-deployment-with-dotclouds-docker-and-strider/)
and even [Heroku buildpacks](https://github.com/progrium/buildstep).
Expect Docker to integrate with even more of your favorite tools going forward, including:
* Alternative storage backends such as ZFS, LVM or [BTRFS](github.com/dotcloud/docker/issues/443)
* Alternative containerization backends such as [OpenVZ](http://openvz.org), Solaris Zones, BSD Jails and even plain Chroot.
* Process managers like [Supervisord](http://supervisord.org/), [Runit](http://smarden.org/runit/), [Gaffer](https://gaffer.readthedocs.org/en/latest/#gaffer) and [Systemd](http://www.freedesktop.org/wiki/Software/systemd/)
* Build and integration tools like Make, Maven, Scons, Jenkins, Buildbot and Cruise Control.
* Configuration management tools like [Puppet](http://puppetlabs.com), [Chef](http://www.opscode.com/chef/) and [Salt](http://saltstack.org)
* Personal development environments like [Vagrant](http://vagrantup.com), [Boxen](http://boxen.github.com/), [Koding](http://koding.com) and [Cloud9](http://c9.io).
* Orchestration tools like [Zookeeper](http://zookeeper.apache.org/), [Mesos](http://incubator.apache.org/mesos/) and [Galaxy](https://github.com/ning/galaxy)
* Infrastructure deployment tools like [Openstack](http://openstack.org), [Apache Cloudstack](http://apache.cloudstack.org), [Ganeti](https://code.google.com/p/ganeti/)
Plugin API
----------
We want Docker to run everywhere, and to integrate with every devops tool.
Those are ambitious goals, and the only way to reach them is with the Docker community.
For the community to participate fully, we need an API which allows Docker to be deeply and easily customized.
We are working on a plugin API which will make Docker very, very customization-friendly.
We believe it will facilitate the integrations listed above - and many more we didn't even think about.
Let us know if you want to start playing with the API before it's generally available.
Externally mounted volumes
--------------------------
In 0.3 we [introduced data volumes](https://github.com/dotcloud/docker/wiki/Docker-0.3.0-release-note%2C-May-6-2013#data-volumes),
a great mechanism for manipulating persistent data such as database files, log files, etc.
Data volumes can be shared between containers, a powerful capability [which allows many advanced use cases](http://docs.docker.io/en/latest/examples/couchdb_data_volumes.html). In the future it will also be possible to share volumes between a container and the underlying host. This will make certain scenarios much easier, such as using a high-performance storage backend for your production database,
making live development changes available to a container, etc.
Better documentation
--------------------
We believe that great documentation is worth 10 features. We are often told that "Docker's documentation is great for a 2-month old project".
Our goal is to make it great, period.
If you have feedback on how to improve our documentation, please get in touch by replying to this email,
or by [filing an issue](https://github.com/dotcloud/docker/issues). We always appreciate it!
Production-ready
----------------
Docker is still alpha software, and not suited for production.
We are working hard to get there, and we are confident that it will be possible within a few months.
Advanced port redirections
--------------------------
Docker currently supports 2 flavors of port redirection: STATIC->STATIC (eg. "redirect public port 80 to private port 80")
and RANDOM->STATIC (eg. "redirect any public port to private port 80").
With these 2 flavors, docker can support the majority of backend programs out there. But some applications have more exotic
requirements, generally to implement custom clustering techniques. These applications include Hadoop, MongoDB, Riak, RabbitMQ,
Disco, and all programs relying on Erlang's OTP.
To support these applications, Docker needs to support more advanced redirection flavors, including:
* RANDOM->RANDOM
* STATIC1->STATIC2
These flavors should be implemented without breaking existing semantics, if at all possible.

2
hack/Vagrantfile vendored
View File

@@ -22,7 +22,7 @@ Vagrant::Config.run do |config|
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; " \
"apt-get install -q -y lxc git aufs-tools 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

View File

@@ -1,23 +1,33 @@
# This will build a container capable of producing an official binary build of docker and
# uploading it to S3
# DESCRIPTION Build a container capable of producing official binary and
# PPA packages and uploading them to S3 and Launchpad
# VERSION 1.2
# DOCKER_VERSION 0.4
# AUTHOR Solomon Hykes <solomon@dotcloud.com>
# Daniel Mizyrycki <daniel@dotcloud.net>
# BUILD_CMD docker build -t dockerbuilder .
# RUN_CMD docker run -e AWS_ID="$AWS_ID" -e AWS_KEY="$AWS_KEY" -e GPG_KEY="$GPG_KEY" dockerbuilder
#
#
from ubuntu:12.04
maintainer Solomon Hykes <solomon@dotcloud.com>
from ubuntu:12.10
# Workaround the upstart issue
run dpkg-divert --local --rename --add /sbin/initctl
run ln -s /bin/true /sbin/initctl
# Enable universe and gophers PPA
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q python-software-properties
run add-apt-repository "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) universe"
run add-apt-repository -y ppa:dotcloud/docker-golang/ubuntu
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.1.linux-amd64.tar.gz
# Packages required to checkout, build and upload docker
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q s3cmd curl
run curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.1.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
run echo "export PATH=/usr/local/go/bin:$PATH" > /.bashrc
run echo "export PATH=/usr/local/go/bin:$PATH" > /.bash_profile
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git 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 DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang-stable debhelper autotools-dev devscripts
# Copy dockerbuilder files into the container
add . /src
run cp /src/dockerbuilder /usr/local/bin/ && chmod +x /usr/local/bin/dockerbuilder
run cp /src/s3cfg /.s3cfg
cmd ["dockerbuilder"]

View File

@@ -0,0 +1 @@
Daniel Mizyrycki <daniel@dotcloud.com>

View File

@@ -2,7 +2,7 @@
set -x
set -e
export PATH=$PATH:/usr/local/go/bin
export PATH=/usr/local/go/bin:$PATH
PACKAGE=github.com/dotcloud/docker
@@ -13,12 +13,10 @@ 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."
if [ -z "$AWS_ID" -o -z "$AWS_KEY" ]; then
echo "Warning: either AWS_ID or AWS_KEY environment variable not set. Won't upload to S3."
else
/bin/echo -e "[default]\naccess_key = $AWS_ID\nsecret_key = $AWS_KEY\n" > /.s3cfg
fi
if [ -z "$GPG_KEY" ]; then
@@ -35,6 +33,10 @@ else
make release RELEASE_VERSION=$REVISION
fi
# Remove credentials from container
rm -f /.s3cfg
if [ -z "$NO_UBUNTU" ]; then
export PATH=`echo $PATH | sed 's#/usr/local/go/bin:##g'`
(cd packaging/ubuntu && make ubuntu)
fi

View File

@@ -1,3 +0,0 @@
#!/bin/sh
echo Whatever you say, man

View File

@@ -1,3 +0,0 @@
[default]
access_key = $AWS_ID
secret_key = $AWS_KEY

View File

@@ -0,0 +1,2 @@
Ken Cochrane <ken@dotcloud.com>
Jerome Petazzoni <jerome@dotcloud.com>

View File

@@ -0,0 +1,5 @@
# Docker project infrastructure
This directory holds all information about the technical infrastructure of the docker project; servers, dns, email, and all the corresponding tools and configuration.
Obviously credentials should not be stored in this repo, but how to obtain and use them should be documented here.

View File

@@ -13,12 +13,13 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"time"
)
type Image struct {
Id string `json:"id"`
ID string `json:"id"`
Parent string `json:"parent,omitempty"`
Comment string `json:"comment,omitempty"`
Created time.Time `json:"created"`
@@ -27,7 +28,9 @@ type Image struct {
DockerVersion string `json:"docker_version,omitempty"`
Author string `json:"author,omitempty"`
Config *Config `json:"config,omitempty"`
Architecture string `json:"architecture,omitempty"`
graph *Graph
Size int64
}
func LoadImage(root string) (*Image, error) {
@@ -41,18 +44,17 @@ func LoadImage(root string) (*Image, error) {
if err := json.Unmarshal(jsonData, img); err != nil {
return nil, err
}
if err := ValidateId(img.Id); err != nil {
if err := ValidateID(img.ID); err != nil {
return nil, err
}
// Check that the filesystem layer exists
if stat, err := os.Stat(layerPath(root)); err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.Id)
} else {
return nil, err
return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
}
return nil, err
} else if !stat.IsDir() {
return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.Id, layerPath(root))
return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.ID, layerPath(root))
}
return img, nil
}
@@ -60,7 +62,7 @@ func LoadImage(root string) (*Image, error) {
func StoreImage(img *Image, layerData Archive, root string, store bool) error {
// Check that root doesn't already exist
if _, err := os.Stat(root); err == nil {
return fmt.Errorf("Image %s already exists", img.Id)
return fmt.Errorf("Image %s already exists", img.ID)
} else if !os.IsNotExist(err) {
return err
}
@@ -94,6 +96,18 @@ func StoreImage(img *Image, layerData Archive, root string, store bool) error {
if err := Untar(layerData, layer); err != nil {
return err
}
return StoreSize(img, root)
}
func StoreSize(img *Image, root string) error {
layer := layerPath(root)
filepath.Walk(layer, func(path string, fileInfo os.FileInfo, err error) error {
img.Size += fileInfo.Size()
return nil
})
// Store the json ball
jsonData, err := json.Marshal(img)
if err != nil {
@@ -126,6 +140,8 @@ func MountAUFS(ro []string, rw string, target string) error {
}
branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
branches += ",xino=/dev/shm/aufs.xino"
//if error, try to load aufs kernel module
if err := mount("none", target, "aufs", 0, branches); err != nil {
log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
@@ -180,11 +196,11 @@ func (image *Image) Changes(rw string) ([]Change, error) {
return Changes(layers, rw)
}
func (image *Image) ShortId() string {
return utils.TruncateId(image.Id)
func (image *Image) ShortID() string {
return utils.TruncateID(image.ID)
}
func ValidateId(id string) error {
func ValidateID(id string) error {
if id == "" {
return fmt.Errorf("Image id can't be empty")
}
@@ -194,7 +210,7 @@ func ValidateId(id string) error {
return nil
}
func GenerateId() string {
func GenerateID() string {
id := make([]byte, 32)
_, err := io.ReadFull(rand.Reader, id)
if err != nil {
@@ -240,7 +256,7 @@ func (img *Image) layers() ([]string, error) {
return nil, e
}
if len(list) == 0 {
return nil, fmt.Errorf("No layer found for image %s\n", img.Id)
return nil, fmt.Errorf("No layer found for image %s\n", img.ID)
}
return list, nil
}
@@ -275,7 +291,7 @@ func (img *Image) root() (string, error) {
if img.graph == nil {
return "", fmt.Errorf("Can't lookup root of unregistered image")
}
return img.graph.imageRoot(img.Id), nil
return img.graph.imageRoot(img.ID), nil
}
// Return the path of an image's layer
@@ -288,8 +304,8 @@ func (img *Image) layer() (string, error) {
}
func (img *Image) Checksum() (string, error) {
img.graph.checksumLock[img.Id].Lock()
defer img.graph.checksumLock[img.Id].Unlock()
img.graph.checksumLock[img.ID].Lock()
defer img.graph.checksumLock[img.ID].Unlock()
root, err := img.root()
if err != nil {
@@ -300,7 +316,7 @@ func (img *Image) Checksum() (string, error) {
if err != nil {
return "", err
}
if checksum, ok := checksums[img.Id]; ok {
if checksum, ok := checksums[img.ID]; ok {
return checksum, nil
}
@@ -351,7 +367,7 @@ func (img *Image) Checksum() (string, error) {
return "", err
}
checksums[img.Id] = hash
checksums[img.ID] = hash
// Dump the checksums to disc
if err := img.graph.storeChecksums(checksums); err != nil {
@@ -361,8 +377,17 @@ func (img *Image) Checksum() (string, error) {
return hash, nil
}
func (img *Image) getParentsSize(size int64) int64 {
parentImage, err := img.GetParent()
if err != nil || parentImage == nil {
return size
}
size += parentImage.Size
return parentImage.getParentsSize(size)
}
// Build an Image object from raw json data
func NewImgJson(src []byte) (*Image, error) {
func NewImgJSON(src []byte) (*Image, error) {
ret := &Image{}
utils.Debugf("Json string: {%s}\n", src)

View File

@@ -19,7 +19,7 @@ lxc.network.flags = up
lxc.network.link = {{.NetworkSettings.Bridge}}
lxc.network.name = eth0
lxc.network.mtu = 1500
lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}}
lxc.network.ipv4 = {{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}
# root filesystem
{{$ROOTFS := .RootfsPath}}
@@ -67,7 +67,11 @@ lxc.cgroup.devices.allow = c 10:200 rwm
# standard mount point
# WARNING: procfs is a known attack vector and should probably be disabled
# if your userspace allows it. eg. see http://blog.zx2c4.com/749
lxc.mount.entry = proc {{$ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
# WARNING: sysfs is a known attack vector and should probably be disabled
# if your userspace allows it. eg. see http://bit.ly/T9CkqJ
lxc.mount.entry = sysfs {{$ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
lxc.mount.entry = devpts {{$ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
#lxc.mount.entry = varrun {{$ROOTFS}}/var/run tmpfs mode=755,size=4096k,nosuid,nodev,noexec 0 0
@@ -86,6 +90,9 @@ lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,rw 0 0
{{end}}
# drop linux capabilities (apply mainly to the user root in the container)
# (Note: 'lxc.cap.keep' is coming soon and should replace this under the
# security principle 'deny all unless explicitly permitted', see
# http://sourceforge.net/mailarchive/message.php?msg_id=31054627 )
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
# limits

View File

@@ -2,13 +2,18 @@ package docker
import (
"fmt"
"github.com/dotcloud/docker/utils"
"os"
"os/exec"
"path/filepath"
"syscall"
"time"
)
func Unmount(target string) error {
if err := exec.Command("auplink", target, "flush").Run(); err != nil {
utils.Debugf("[warning]: couldn't run auplink before unmount: %s", err)
}
if err := syscall.Unmount(target, 0); err != nil {
return err
}

View File

@@ -52,7 +52,7 @@ func ipToInt(ip net.IP) int32 {
}
// Converts 32 bit integer into a 4 bytes IP address
func intToIp(n int32) net.IP {
func intToIP(n int32) net.IP {
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, uint32(n))
return net.IP(b)
@@ -132,9 +132,8 @@ func CreateBridgeIface(ifaceName string) error {
}
if ifaceAddr == "" {
return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName)
} else {
utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
}
utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil {
return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output)
@@ -258,7 +257,6 @@ func proxy(listener net.Listener, proto, address string) error {
utils.Debugf("Connected to backend, splicing")
splice(src, dst)
}
return nil
}
func halfSplice(dst, src net.Conn) error {
@@ -398,7 +396,7 @@ func (alloc *IPAllocator) run() {
}
}
ip := allocatedIP{ip: intToIp(newNum)}
ip := allocatedIP{ip: intToIP(newNum)}
if inUse {
ip.err = errors.New("No unallocated IP available")
}
@@ -465,11 +463,11 @@ func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
return nil, err
}
// Allocate a random port if Frontend==0
if extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend); err != nil {
extPort, err := iface.manager.portAllocator.Acquire(nat.Frontend)
if err != nil {
return nil, err
} else {
nat.Frontend = extPort
}
nat.Frontend = extPort
if err := iface.manager.portMapper.Map(nat.Frontend, net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}); err != nil {
iface.manager.portAllocator.Release(nat.Frontend)
return nil, err
@@ -486,20 +484,38 @@ type Nat struct {
func parseNat(spec string) (*Nat, error) {
var nat Nat
// If spec starts with ':', external and internal ports must be the same.
// This might fail if the requested external port is not available.
var sameFrontend bool
if spec[0] == ':' {
sameFrontend = true
spec = spec[1:]
}
port, err := strconv.ParseUint(spec, 10, 16)
if err != nil {
return nil, err
}
nat.Backend = int(port)
if sameFrontend {
nat.Frontend = nat.Backend
if strings.Contains(spec, ":") {
specParts := strings.Split(spec, ":")
if len(specParts) != 2 {
return nil, fmt.Errorf("Invalid port format.")
}
// If spec starts with ':', external and internal ports must be the same.
// This might fail if the requested external port is not available.
var sameFrontend bool
if len(specParts[0]) == 0 {
sameFrontend = true
} else {
front, err := strconv.ParseUint(specParts[0], 10, 16)
if err != nil {
return nil, err
}
nat.Frontend = int(front)
}
back, err := strconv.ParseUint(specParts[1], 10, 16)
if err != nil {
return nil, err
}
nat.Backend = int(back)
if sameFrontend {
nat.Frontend = nat.Backend
}
} else {
port, err := strconv.ParseUint(spec, 10, 16)
if err != nil {
return nil, err
}
nat.Backend = int(port)
}
nat.Proto = "tcp"
return &nat, nil

View File

@@ -18,6 +18,32 @@ func TestIptables(t *testing.T) {
}
}
func TestParseNat(t *testing.T) {
if nat, err := parseNat("4500"); err == nil {
if nat.Frontend != 0 || nat.Backend != 4500 {
t.Errorf("-p 4500 should produce 0->4500, got %d->%d", nat.Frontend, nat.Backend)
}
} else {
t.Fatal(err)
}
if nat, err := parseNat(":4501"); err == nil {
if nat.Frontend != 4501 || nat.Backend != 4501 {
t.Errorf("-p :4501 should produce 4501->4501, got %d->%d", nat.Frontend, nat.Backend)
}
} else {
t.Fatal(err)
}
if nat, err := parseNat("4502:4503"); err == nil {
if nat.Frontend != 4502 || nat.Backend != 4503 {
t.Errorf("-p 4502:4503 should produce 4502->4503, got %d->%d", nat.Frontend, nat.Backend)
}
} else {
t.Fatal(err)
}
}
func TestPortAllocation(t *testing.T) {
allocator, err := newPortAllocator()
if err != nil {
@@ -137,7 +163,7 @@ func TestConversion(t *testing.T) {
if i == 0 {
t.Fatal("converted to zero")
}
conv := intToIp(i)
conv := intToIP(i)
if !ip.Equal(conv) {
t.Error(conv.String())
}

Some files were not shown because too many files have changed in this diff Show More