Browse code

Merge pull request #2577 from dotcloud/bump_v0.6.6

Bump v0.6.6

Victor Vieux authored on 2013/11/07 05:03:03
Showing 86 changed files
... ...
@@ -69,12 +69,14 @@ Gabriel Monroy <gabriel@opdemand.com>
69 69
 Gareth Rushgrove <gareth@morethanseven.net>
70 70
 Greg Thornton <xdissent@me.com>
71 71
 Guillaume J. Charmes <guillaume.charmes@dotcloud.com>
72
+Gurjeet Singh <gurjeet@singh.im>
72 73
 Guruprasad <lgp171188@gmail.com>
73 74
 Harley Laue <losinggeneration@gmail.com>
74 75
 Hector Castro <hectcastro@gmail.com>
75 76
 Hunter Blanks <hunter@twilio.com>
76 77
 Isao Jonas <isao.jonas@gmail.com>
77 78
 James Carr <james.r.carr@gmail.com>
79
+James Turnbull <james@lovedthanlost.net>
78 80
 Jason McVetta <jason.mcvetta@gmail.com>
79 81
 Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
80 82
 Jeff Lindsay <progrium@gmail.com>
... ...
@@ -140,6 +142,7 @@ odk- <github@odkurzacz.org>
140 140
 Pascal Borreli <pascal@borreli.com>
141 141
 Paul Bowsher <pbowsher@globalpersonals.co.uk>
142 142
 Paul Hammond <paul@paulhammond.org>
143
+Paul Nasrat <pnasrat@gmail.com>
143 144
 Phil Spitler <pspitler@gmail.com>
144 145
 Piotr Bogdan <ppbogdan@gmail.com>
145 146
 pysqz <randomq@126.com>
... ...
@@ -1,514 +1,783 @@
1 1
 # Changelog
2 2
 
3
+## 0.6.6 (2013-11-06)
4
+
5
+#### Runtime
6
+
7
+* Ensure container name on register
8
+* Fix regression in /etc/hosts
9
++ Add lock around write operations in graph
10
+* Check if port is valid
11
+* Fix restart runtime error with ghost container networking
12
++ Added some more colors and animals to increase the pool of generated names
13
+* Fix issues in docker inspect
14
++ Escape apparmor confinement
15
++ Set environment variables using a file.
16
+* Prevent docker insert to erase something
17
++ Prevent DNS server conflicts in CreateBridgeIface
18
++ Validate bind mounts on the server side
19
++ Use parent image config in docker build
20
+* Fix regression in /etc/hosts
21
+
22
+#### Client
23
+
24
++ Add -P flag to publish all exposed ports
25
++ Add -notrunc and -q flags to docker history
26
+* Fix docker commit, tag and import usage
27
++ Add stars, trusted builds and library flags in docker search
28
+* Fix docker logs with tty
29
+
30
+#### RemoteAPI
31
+
32
+* Make /events API send headers immediately
33
+* Do not split last column docker top
34
++ Add size to history
35
+
36
+#### Other
37
+
38
++ Contrib: Desktop integration. Firefox usecase.
39
++ Dockerfile: bump to go1.2rc3
40
+
3 41
 ## 0.6.5 (2013-10-29)
4
-+ Runtime: Containers can now be named
5
-+ Runtime: Containers can now be linked together for service discovery
6
-+ Runtime: 'run -a', 'start -a' and 'attach' can forward signals to the container for better integration with process supervisors
7
-+ Runtime: Automatically start crashed containers after a reboot
8
-+ Runtime: Expose IP, port, and proto as separate environment vars for container links
9
-* Runtime: Allow ports to be published to specific ips
10
-* Runtime: Prohibit inter-container communication by default
42
+
43
+#### Runtime
44
+
45
++ Containers can now be named
46
++ Containers can now be linked together for service discovery
47
++ 'run -a', 'start -a' and 'attach' can forward signals to the container for better integration with process supervisors
48
++ Automatically start crashed containers after a reboot
49
++ Expose IP, port, and proto as separate environment vars for container links
50
+* Allow ports to be published to specific ips
51
+* Prohibit inter-container communication by default
52
+- Ignore ErrClosedPipe for stdin in Container.Attach
53
+- Remove unused field kernelVersion
54
+* Fix issue when mounting subdirectories of /mnt in container
55
+- Fix untag during removal of images
56
+* Check return value of syscall.Chdir when changing working directory inside dockerinit
57
+
58
+#### Client
59
+
60
+- Only pass stdin to hijack when needed to avoid closed pipe errors
61
+* Use less reflection in command-line method invocation
62
+- Monitor the tty size after starting the container, not prior
63
+- Remove useless os.Exit() calls after log.Fatal
64
+
65
+#### Hack
66
+
67
++ Add initial init scripts library and a safer Ubuntu packaging script that works for Debian
68
+* Add -p option to invoke debootstrap with http_proxy
69
+- Update install.sh with $sh_c to get sudo/su for modprobe
70
+* Update all the mkimage scripts to use --numeric-owner as a tar argument
71
+* Update hack/release.sh process to automatically invoke hack/make.sh and bail on build and test issues
72
+
73
+#### Other
74
+
11 75
 * Documentation: Fix the flags for nc in example
12
-- Client: Only pass stdin to hijack when needed to avoid closed pipe errors
13 76
 * Testing: Remove warnings and prevent mount issues
14
-* Client: Use less reflection in command-line method invocation
15
-- Runtime: Ignore ErrClosedPipe for stdin in Container.Attach
16 77
 - Testing: Change logic for tty resize to avoid warning in tests
17
-- Client: Monitor the tty size after starting the container, not prior
18
-- Hack: Update install.sh with $sh_c to get sudo/su for modprobe
19
-- Client: Remove useless os.Exit() calls after log.Fatal
20
-* Hack: Update all the mkimage scripts to use --numeric-owner as a tar argument
21
-* Hack: Update hack/release.sh process to automatically invoke hack/make.sh and bail on build and test issues
22
-+ Hack: Add initial init scripts library and a safer Ubuntu packaging script that works for Debian
23
-- Runtime: Fix untag during removal of images 
24
-- Runtime: Remove unused field kernelVersion 
25
-* Hack: Add -p option to invoke debootstrap with http_proxy
26 78
 - Builder: Fix race condition in docker build with verbose output
27 79
 - Registry: Fix content-type for PushImageJSONIndex method
28
-* Runtime: Fix issue when mounting subdirectories of /mnt in container
29
-* Runtime: Check return value of syscall.Chdir when changing working directory inside dockerinit
30 80
 * Contrib: Improve helper tools to generate debian and Arch linux server images
31 81
 
32 82
 ## 0.6.4 (2013-10-16)
33
-- Runtime: Add cleanup of container when Start() fails
34
-- Testing: Catch errClosing error when TCP and UDP proxies are terminated
35
-- Testing: Add aggregated docker-ci email report
36
-- Testing: Remove a few errors in tests
37
-* Contrib: Reorganize contributed completion scripts to add zsh completion
38
-* Contrib: Add vim syntax highlighting for Dockerfiles from @honza
39
-* Runtime: Add better comments to utils/stdcopy.go
40
-- Testing: add cleanup to remove leftover containers
41
-* Documentation: Document how to edit and release docs
42
-* Documentation: Add initial draft of the Docker infrastructure doc
43
-* Contrib: Add mkimage-arch.sh
83
+
84
+#### Runtime
85
+
86
+- Add cleanup of container when Start() fails
87
+* Add better comments to utils/stdcopy.go
88
+* Add utils.Errorf for error logging
89
++ Add -rm to docker run for removing a container on exit
90
+- Remove error messages which are not actually errors
91
+- Fix `docker rm` with volumes
92
+- Fix some error cases where a HTTP body might not be closed
93
+- Fix panic with wrong dockercfg file
94
+- Fix the attach behavior with -i
95
+* Record termination time in state.
96
+- Use empty string so TempDir uses the OS's temp dir automatically
97
+- Make sure to close the network allocators
98
++ Autorestart containers by default
99
+* Bump vendor kr/pty to commit 3b1f6487b `(syscall.O_NOCTTY)`
100
+* lxc: Allow set_file_cap capability in container
101
+- Move run -rm to the cli only
102
+* Split stdout stderr
103
+* Always create a new session for the container
104
+
105
+#### Testing
106
+
107
+- Add aggregated docker-ci email report
108
+- Add cleanup to remove leftover containers
109
+* Add nightly release to docker-ci
110
+* Add more tests around auth.ResolveAuthConfig
111
+- Remove a few errors in tests
112
+- Catch errClosing error when TCP and UDP proxies are terminated
113
+* Only run certain tests with TESTFLAGS='-run TestName' make.sh
114
+* Prevent docker-ci to test closing PRs
115
+* Replace panic by log.Fatal in tests
116
+- Increase TestRunDetach timeout
117
+
118
+#### Documentation
119
+
120
+* Add initial draft of the Docker infrastructure doc
121
+* Add devenvironment link to CONTRIBUTING.md
122
+* Add `apt-get install curl` to Ubuntu docs
123
+* Add explanation for export restrictions
124
+* Add .dockercfg doc
125
+* Remove Gentoo install notes about #1422 workaround
126
+* Fix help text for -v option
127
+* Fix Ping endpoint documentation
128
+- Fix parameter names in docs for ADD command
129
+- Fix ironic typo in changelog
130
+* Various command fixes in postgres example
131
+* Document how to edit and release docs
132
+- Minor updates to `postgresql_service.rst`
133
+* Clarify LGTM process to contributors
134
+- Corrected error in the package name
135
+* Document what `vagrant up` is actually doing
136
++ improve doc search results
137
+* Cleanup whitespace in API 1.5 docs
138
+* use angle brackets in MAINTAINER example email
139
+* Update archlinux.rst
140
++ Changes to a new style for the docs. Includes version switcher.
141
+* Formatting, add information about multiline json
142
+* Improve registry and index REST API documentation
143
+- Replace deprecated upgrading reference to docker-latest.tgz, which hasn't been updated since 0.5.3
144
+* Update Gentoo installation documentation now that we're in the portage tree proper
145
+* Cleanup and reorganize docs and tooling for contributors and maintainers
146
+- Minor spelling correction of protocoll -> protocol
147
+
148
+#### Contrib
149
+
150
+* Add vim syntax highlighting for Dockerfiles from @honza
151
+* Add mkimage-arch.sh
152
+* Reorganize contributed completion scripts to add zsh completion
153
+
154
+#### Hack
155
+
156
+* Add vagrant user to the docker group
157
+* Add proper bash completion for "docker push"
158
+* Add xz utils as a runtime dep
159
+* Add cleanup/refactor portion of #2010 for hack and Dockerfile updates
160
++ Add contrib/mkimage-centos.sh back (from #1621), and associated documentation link
161
+* Add several of the small make.sh fixes from #1920, and make the output more consistent and contributor-friendly
162
++ Add @tianon to hack/MAINTAINERS
163
+* Improve network performance for VirtualBox
164
+* Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.)
165
+- Fix contrib/mkimage-debian.sh apt caching prevention
166
++ Added Dockerfile.tmLanguage to contrib
167
+* Configured FPM to make /etc/init/docker.conf a config file
168
+* Enable SSH Agent forwarding in Vagrant VM
169
+* Several small tweaks/fixes for contrib/mkimage-debian.sh
170
+
171
+#### Other
172
+
44 173
 - Builder: Abort build if mergeConfig returns an error and fix duplicate error message
45
-- Runtime: Remove error messages which are not actually errors
46
-* Testing: Only run certain tests with TESTFLAGS='-run TestName' make.sh
47
-* Testing: Prevent docker-ci to test closing PRs
48
-- Documentation: Minor updates to postgresql_service.rst
49
-* Testing: Add nightly release to docker-ci
50
-* Hack: Improve network performance for VirtualBox
51
-* Hack: Add vagrant user to the docker group
52
-* Runtime: Add utils.Errorf for error logging
53 174
 - Packaging: Remove deprecated packaging directory
54
-* Hack: Revamp install.sh to be usable by more people, and to use official install methods whenever possible (apt repo, portage tree, etc.)
55
-- Hack: Fix contrib/mkimage-debian.sh apt caching prevention
56
-* Documentation: Clarify LGTM process to contributors
57
-- Documentation: Small fixes to parameter names in docs for ADD command
58
-* Runtime: Record termination time in state.
59 175
 - Registry: Use correct auth config when logging in.
60
-- Documentation: Corrected error in the package name
61
-* Documentation: Document what `vagrant up` is actually doing
62
-- Runtime: Fix `docker rm` with volumes
63
-- Runtime: Use empty string so TempDir uses the OS's temp dir automatically
64
-- Runtime: Make sure to close the network allocators
65
-* Testing: Replace panic by log.Fatal in tests
66
-+ Documentation: improve doc search results
67
-- Runtime: Fix some error cases where a HTTP body might not be closed
68
-* Hack: Add proper bash completion for "docker push"
69
-* Documentation: Add devenvironment link to CONTRIBUTING.md
70
-* Documentation: Cleanup whitespace in API 1.5 docs
71
-* Documentation: use angle brackets in MAINTAINER example email
72
-- Testing: Increase TestRunDetach timeout
73
-* Documentation: Fix help text for -v option
74
-+ Hack: Added Dockerfile.tmLanguage to contrib
75
-+ Runtime: Autorestart containers by default
76
-* Testing: Adding more tests around auth.ResolveAuthConfig
77
-* Hack: Configured FPM to make /etc/init/docker.conf a config file
78
-* Hack: Add xz utils as a runtime dep
79
-* Documentation: Add `apt-get install curl` to Ubuntu docs
80
-* Documentation: Remove Gentoo install notes about #1422 workaround
81
-* Documentation: Fix Ping endpoint documentation
82
-* Runtime: Bump vendor kr/pty to commit 3b1f6487b (syscall.O_NOCTTY)
83
-* Runtime: lxc: Allow set_file_cap capability in container
84
-* Documentation: Update archlinux.rst
85
-- Documentation: Fix ironic typo in changelog
86
-* Documentation: Add explanation for export restrictions
87
-* Hack: Add cleanup/refactor portion of #2010 for hack and Dockerfile updates
88
-+ Documentation: Changes to a new style for the docs. Includes version switcher.
89
-* Documentation: Formatting, add information about multiline json
90
-+ Hack: Add contrib/mkimage-centos.sh back (from #1621), and associated documentation link
91
-- Runtime: Fix panic with wrong dockercfg file
92
-- Runtime: Fix the attach behavior with -i
93
-* Documentation: Add .dockercfg doc
94
-- Runtime: Move run -rm to the cli only
95
-* Hack: Enable SSH Agent forwarding in Vagrant VM
96
-+ Runtime: Add -rm to docker run for removing a container on exit
97
-* Documentation: Improve registry and index REST API documentation
98
-* Runtime: Split stdout stderr
99
-- Documentation: Replace deprecated upgrading reference to docker-latest.tgz, which hasn't been updated since 0.5.3
100
-* Documentation: Update Gentoo installation documentation now that we're in the portage tree proper
101 176
 - Registry: Fix the error message so it is the same as the regex
102
-* Runtime: Always create a new session for the container
103
-* Hack: Add several of the small make.sh fixes from #1920, and make the output more consistent and contributor-friendly
104
-* Documentation: Various command fixes in postgres example
105
-* Documentation: Cleanup and reorganize docs and tooling for contributors and maintainers
106
-- Documentation: Minor spelling correction of protocoll -> protocol
107
-* Hack: Several small tweaks/fixes for contrib/mkimage-debian.sh
108
-+ Hack: Add @tianon to hack/MAINTAINERS
109 177
 
110 178
 ## 0.6.3 (2013-09-23)
111
-* Packaging: Update tar vendor dependency
179
+
180
+#### Packaging
181
+
182
+* Add 'docker' group on install for ubuntu package
183
+* Update tar vendor dependency
184
+* Download apt key over HTTPS
185
+
186
+#### Runtime
187
+
188
+- Only copy and change permissions on non-bindmount volumes
189
+* Allow multiple volumes-from
190
+- Fix HTTP imports from STDIN
191
+
192
+#### Documentation
193
+
194
+* Update section on extracting the docker binary after build
195
+* Update development environment docs for new build process
196
+* Remove 'base' image from documentation
197
+
198
+#### Other
199
+
112 200
 - Client: Fix detach issue
113
-- Runtime: Only copy and change permissions on non-bindmount volumes
114 201
 - Registry: Update regular expression to match index
115
-* Runtime: Allow multiple volumes-from
116
-* Packaging: Download apt key over HTTPS
117
-* Documentation: Update section on extracting the docker binary after build
118
-* Documentation: Update development environment docs for new build process
119
-* Documentation: Remove 'base' image from documentation
120
-* Packaging: Add 'docker' group on install for ubuntu package
121
-- Runtime: Fix HTTP imports from STDIN
122 202
 
123 203
 ## 0.6.2 (2013-09-17)
204
+
205
+#### Runtime
206
+
207
++ Add domainname support
208
++ Implement image filtering with path.Match
209
+* Remove unnecesasry warnings
210
+* Remove os/user dependency
211
+* Only mount the hostname file when the config exists
212
+* Handle signals within the `docker login` command
213
+- UID and GID are now also applied to volumes
214
+- `docker start` set error code upon error
215
+- `docker run` set the same error code as the process started
216
+
217
+#### Builder
218
+
219
++ Add -rm option in order to remove intermediate containers
220
+* Allow multiline for the RUN instruction
221
+
222
+#### Registry
223
+
224
+* Implement login with private registry
225
+- Fix push issues
226
+
227
+#### Other
228
+
124 229
 + Hack: Vendor all dependencies
125
-+ Builder: Add -rm option in order to remove intermediate containers
126
-+ Runtime: Add domainname support
127
-+ Runtime: Implement image filtering with path.Match
128
-* Builder: Allow multiline for the RUN instruction
129
-* Runtime: Remove unnecesasry warnings
130
-* Runtime: Only mount the hostname file when the config exists
131
-* Runtime: Handle signals within the `docker login` command
132
-* Runtime: Remove os/user dependency
133
-* Registry: Implement login with private registry
134 230
 * Remote API: Bump to v1.5
135 231
 * Packaging: Break down hack/make.sh into small scripts, one per 'bundle': test, binary, ubuntu etc.
136
-* Documentation: General improvements
137
-- Runtime: UID and GID are now also applied to volumes
138
-- Runtime: `docker start` set error code upon error
139
-- Runtime: `docker run` set the same error code as the process started
140
-- Registry: Fix push issues
232
+* Documentation: General improvments
141 233
 
142 234
 ## 0.6.1 (2013-08-23)
143
-* Registry: Pass "meta" headers in API calls to the registry
144
-- Packaging: Use correct upstart script with new build tool
145
-- Packaging: Use libffi-dev, don't build it from sources
146
-- Packaging: Removed duplicate mercurial install command
235
+
236
+#### Registry
237
+
238
+* Pass "meta" headers in API calls to the registry
239
+
240
+#### Packaging
241
+
242
+- Use correct upstart script with new build tool
243
+- Use libffi-dev, don`t build it from sources
244
+- Remove duplicate mercurial install command
147 245
 
148 246
 ## 0.6.0 (2013-08-22)
149
-- Runtime: Load authConfig only when needed and fix useless WARNING
150
-+ Runtime: Add lxc-conf flag to allow custom lxc options
151
-- Runtime: Fix race conditions in parallel pull
152
-- Runtime: Improve CMD, ENTRYPOINT, and attach docs.
153
-* Documentation: Small fix to docs regarding adding docker groups
154
-* Documentation: Add MongoDB image example
155
-+ Builder: Add USER instruction do Dockerfile
156
-* Documentation: updated default -H docs
157
-* Remote API: Sort Images by most recent creation date.
158
-+ Builder: Add workdir support for the Buildfile
159
-+ Runtime: Add an option to set the working directory
160
-- Runtime: Show tag used when image is missing
161
-* Documentation: Update readme with dependencies for building
162
-* Documentation: Add instructions for creating and using the docker group
163
-* Remote API: Reworking opaque requests in registry module
164
-- Runtime: Fix Graph ByParent() to generate list of child images per parent image.
165
-* Runtime: Add Image name to LogEvent tests
166
-* Documentation: Add sudo to examples and installation to documentation
167
-+ Hack: Bash Completion: Limit commands to containers of a relevant state
168
-* Remote API: Add image name in /events
169
-* Runtime: Apply volumes-from before creating volumes
170
-- Runtime: Make docker run handle SIGINT/SIGTERM
171
-- Runtime: Prevent crash when .dockercfg not readable
172
-* Hack: Add docker dependencies coverage testing into docker-ci
173
-+ Runtime: Add -privileged flag and relevant tests, docs, and examples
174
-+ Packaging: Docker-brew 0.5.2 support and memory footprint reduction
175
-- Runtime: Install script should be fetched over https, not http.
176
-* Packaging: Add new docker dependencies into docker-ci
177
-* Runtime: Use Go 1.1.2 for dockerbuilder
178
-* Registry: Improve auth push
179
-* Runtime: API, issue 1471: Use groups for socket permissions
180
-* Documentation: PostgreSQL service example in documentation
247
+
248
+#### Runtime
249
+
250
++ Add lxc-conf flag to allow custom lxc options
251
++ Add an option to set the working directory
252
+* Add Image name to LogEvent tests
253
++ Add -privileged flag and relevant tests, docs, and examples
254
+* Add websocket support to /container/<name>/attach/ws
255
+* Add warning when net.ipv4.ip_forwarding = 0
256
+* Add hostname to environment
257
+* Add last stable version in `docker version`
258
+- Fix race conditions in parallel pull
259
+- Fix Graph ByParent() to generate list of child images per parent image.
260
+- Fix typo: fmt.Sprint -> fmt.Sprintf
261
+- Fix small \n error un docker build
262
+* Fix to "Inject dockerinit at /.dockerinit"
263
+* Fix #910. print user name to docker info output
264
+* Use Go 1.1.2 for dockerbuilder
265
+* Use ranged for loop on channels
266
+- Use utils.ParseRepositoryTag instead of strings.Split(name, ":") in server.ImageDelete
267
+- Improve CMD, ENTRYPOINT, and attach docs.
268
+- Improve connect message with socket error
269
+- Load authConfig only when needed and fix useless WARNING
270
+- Show tag used when image is missing
271
+* Apply volumes-from before creating volumes
272
+- Make docker run handle SIGINT/SIGTERM
273
+- Prevent crash when .dockercfg not readable
274
+- Install script should be fetched over https, not http.
275
+* API, issue 1471: Use groups for socket permissions
276
+- Correctly detect IPv4 forwarding
277
+* Mount /dev/shm as a tmpfs
278
+- Switch from http to https for get.docker.io
279
+* Let userland proxy handle container-bound traffic
280
+* Updated the Docker CLI to specify a value for the "Host" header.
281
+- Change network range to avoid conflict with EC2 DNS
282
+- Reduce connect and read timeout when pinging the registry
283
+* Parallel pull
284
+- Handle ip route showing mask-less IP addresses
285
+* Allow ENTRYPOINT without CMD
286
+- Always consider localhost as a domain name when parsing the FQN repos name
287
+* Refactor checksum
288
+
289
+#### Documentation
290
+
291
+* Add MongoDB image example
292
+* Add instructions for creating and using the docker group
293
+* Add sudo to examples and installation to documentation
294
+* Add ufw doc
295
+* Add a reference to ps -a
296
+* Add information about Docker`s high level tools over LXC.
297
+* Fix typo in docs for docker run -dns
298
+* Fix a typo in the ubuntu installation guide
299
+* Fix to docs regarding adding docker groups
300
+* Update default -H docs
301
+* Update readme with dependencies for building
302
+* Update amazon.rst to explain that Vagrant is not necessary for running Docker on ec2
303
+* PostgreSQL service example in documentation
304
+* Suggest installing linux-headers by default.
305
+* Change the twitter handle
306
+* Clarify Amazon EC2 installation
307
+* 'Base' image is deprecated and should no longer be referenced in the docs.
308
+* Move note about officially supported kernel
309
+- Solved the logo being squished in Safari
310
+
311
+#### Builder
312
+
313
++ Add USER instruction do Dockerfile
314
++ Add workdir support for the Buildfile
315
+* Add no cache for docker build
316
+- Fix docker build and docker events output
317
+- Only count known instructions as build steps
318
+- Make sure ENV instruction within build perform a commit each time
319
+- Forbid certain paths within docker build ADD
320
+- Repository name (and optionally a tag) in build usage
321
+- Make sure ADD will create everything in 0755
322
+
323
+#### Remote API
324
+
325
+* Sort Images by most recent creation date.
326
+* Reworking opaque requests in registry module
327
+* Add image name in /events
328
+* Use mime pkg to parse Content-Type
329
+* 650 http utils and user agent field
330
+
331
+#### Hack
332
+
333
++ Bash Completion: Limit commands to containers of a relevant state
334
+* Add docker dependencies coverage testing into docker-ci
335
+
336
+#### Packaging
337
+
338
++ Docker-brew 0.5.2 support and memory footprint reduction
339
+* Add new docker dependencies into docker-ci
340
+- Revert "docker.upstart: avoid spawning a `sh` process"
341
++ Docker-brew and Docker standard library
342
++ Release docker with docker
343
+* Fix the upstart script generated by get.docker.io
344
+* Enabled the docs to generate manpages.
345
+* Revert Bind daemon to 0.0.0.0 in Vagrant.
346
+
347
+#### Register
348
+
349
+* Improve auth push
350
+* Registry unit tests + mock registry
351
+
352
+#### Tests
353
+
354
+* Improve TestKillDifferentUser to prevent timeout on buildbot
355
+- Fix typo in TestBindMounts (runContainer called without image)
356
+* Improve TestGetContainersTop so it does not rely on sleep
357
+* Relax the lo interface test to allow iface index != 1
358
+* Add registry functional test to docker-ci
359
+* Add some tests in server and utils
360
+
361
+#### Other
362
+
181 363
 * Contrib: bash completion script
182
-* Tests: Improve TestKillDifferentUser to prevent timeout on buildbot
183
-* Documentation: Fix typo in docs for docker run -dns
184
-* Documentation: Adding a reference to ps -a
185
-- Runtime: Correctly detect IPv4 forwarding
186
-- Packaging: Revert "docker.upstart: avoid spawning a `sh` process"
187
-* Runtime: Use ranged for loop on channels
188
-- Runtime: Fix typo: fmt.Sprint -> fmt.Sprintf
189
-- Tests: Fix typo in TestBindMounts (runContainer called without image)
190
-* Runtime: add websocket support to /container/<name>/attach/ws
191
-* Runtime: Mount /dev/shm as a tmpfs
192
-- Builder: Only count known instructions as build steps
193
-- Builder: Fix docker build and docker events output
194
-- Runtime: switch from http to https for get.docker.io
195
-* Tests: Improve TestGetContainersTop so it does not rely on sleep
196
-+ Packaging: Docker-brew and Docker standard library
197
-* Testing: Add some tests in server and utils
198
-+ Packaging: Release docker with docker
199
-- Builder: Make sure ENV instruction within build perform a commit each time
200
-* Packaging: Fix the upstart script generated by get.docker.io
201
-- Runtime: fix small \n error un docker build
202
-* Runtime: Let userland proxy handle container-bound traffic
203
-* Runtime: Updated the Docker CLI to specify a value for the "Host" header.
204
-* Runtime: Add warning when net.ipv4.ip_forwarding = 0
205
-* Registry: Registry unit tests + mock registry
206
-* Runtime: fixed #910. print user name to docker info output
207
-- Builder: Forbid certain paths within docker build ADD
208
-- Runtime: change network range to avoid conflict with EC2 DNS
209
-* Tests: Relax the lo interface test to allow iface index != 1
210
-* Documentation: Suggest installing linux-headers by default.
211
-* Documentation: Change the twitter handle
212 364
 * Client: Add docker cp command and copy api endpoint to copy container files/folders to the host
213
-* Remote API: Use mime pkg to parse Content-Type
214
-- Runtime: Reduce connect and read timeout when pinging the registry
215
-* Documentation: Update amazon.rst to explain that Vagrant is not necessary for running Docker on ec2
216
-* Packaging: Enabled the docs to generate manpages.
217
-* Runtime: Parallel pull
218
-- Runtime: Handle ip route showing mask-less IP addresses
219
-* Documentation: Clarify Amazon EC2 installation
220
-* Documentation: 'Base' image is deprecated and should no longer be referenced in the docs.
221
-* Runtime: Fix to "Inject dockerinit at /.dockerinit"
222
-* Runtime: Allow ENTRYPOINT without CMD
223
-- Runtime: Always consider localhost as a domain name when parsing the FQN repos name
224
-* Remote API: 650 http utils and user agent field
225
-* Documentation: fix a typo in the ubuntu installation guide
226
-- Builder: Repository name (and optionally a tag) in build usage
227
-* Documentation: Move note about officially supported kernel
228
-* Packaging: Revert "Bind daemon to 0.0.0.0 in Vagrant.
229
-* Builder: Add no cache for docker build
230
-* Runtime: Add hostname to environment
231
-* Runtime: Add last stable version in `docker version`
232
-- Builder: Make sure ADD will create everything in 0755
233
-* Documentation: Add ufw doc
234
-* Tests: Add registry functional test to docker-ci
235
-- Documentation: Solved the logo being squished in Safari
236
-- Runtime: Use utils.ParseRepositoryTag instead of strings.Split(name, ":") in server.ImageDelete
237
-* Runtime: Refactor checksum
238
-- Runtime: Improve connect message with socket error
239
-* Documentation: Added information about Docker's high level tools over LXC.
240
-* Don't read from stdout when only attached to stdin
365
+* Don`t read from stdout when only attached to stdin
241 366
 
242 367
 ## 0.5.3 (2013-08-13)
243
-* Runtime: Use docker group for socket permissions
244
-- Runtime: Spawn shell within upstart script
245
-- Builder: Make sure ENV instruction within build perform a commit each time
246
-- Runtime: Handle ip route showing mask-less IP addresses
247
-- Runtime: Add hostname to environment
368
+
369
+#### Runtime
370
+
371
+* Use docker group for socket permissions
372
+- Spawn shell within upstart script
373
+- Handle ip route showing mask-less IP addresses
374
+- Add hostname to environment
375
+
376
+#### Builder
377
+
378
+- Make sure ENV instruction within build perform a commit each time
248 379
 
249 380
 ## 0.5.2 (2013-08-08)
250
- * Builder: Forbid certain paths within docker build ADD
251
- - Runtime: Change network range to avoid conflict with EC2 DNS
252
- * API: Change daemon to listen on unix socket by default
381
+
382
+* Builder: Forbid certain paths within docker build ADD
383
+- Runtime: Change network range to avoid conflict with EC2 DNS
384
+* API: Change daemon to listen on unix socket by default
253 385
 
254 386
 ## 0.5.1 (2013-07-30)
255
- + API: Docker client now sets useragent (RFC 2616)
256
- + Runtime: Add `ps` args to `docker top`
257
- + Runtime: Add support for container ID files (pidfile like)
258
- + Runtime: Add container=lxc in default env
259
- + Runtime: Support networkless containers with `docker run -n` and `docker -d -b=none`
260
- + API: Add /events endpoint
261
- + Builder: ADD command now understands URLs
262
- + Builder: CmdAdd and CmdEnv now respect Dockerfile-set ENV variables
263
- * Hack: Simplify unit tests with helpers
264
- * Hack: Improve docker.upstart event
265
- * Hack: Add coverage testing into docker-ci
266
- * Runtime: Stdout/stderr logs are now stored in the same file as JSON
267
- * Runtime: Allocate a /16 IP range by default, with fallback to /24. Try 12 ranges instead of 3.
268
- * Runtime: Change .dockercfg format to json and support multiple auth remote
269
- - Runtime: Do not override volumes from config
270
- - Runtime: Fix issue with EXPOSE override
271
- - Builder: Create directories with 755 instead of 700 within ADD instruction
387
+
388
+#### Runtime
389
+
390
++ Add `ps` args to `docker top`
391
++ Add support for container ID files (pidfile like)
392
++ Add container=lxc in default env
393
++ Support networkless containers with `docker run -n` and `docker -d -b=none`
394
+* Stdout/stderr logs are now stored in the same file as JSON
395
+* Allocate a /16 IP range by default, with fallback to /24. Try 12 ranges instead of 3.
396
+* Change .dockercfg format to json and support multiple auth remote
397
+- Do not override volumes from config
398
+- Fix issue with EXPOSE override
399
+
400
+#### API
401
+
402
++ Docker client now sets useragent (RFC 2616)
403
++ Add /events endpoint
404
+
405
+#### Builder
406
+
407
++ ADD command now understands URLs
408
++ CmdAdd and CmdEnv now respect Dockerfile-set ENV variables
409
+- Create directories with 755 instead of 700 within ADD instruction
410
+
411
+#### Hack
412
+
413
+* Simplify unit tests with helpers
414
+* Improve docker.upstart event
415
+* Add coverage testing into docker-ci
272 416
 
273 417
 ## 0.5.0 (2013-07-17)
274
- + Runtime: List all processes running inside a container with 'docker top'
275
- + Runtime: Host directories can be mounted as volumes with 'docker run -v'
276
- + Runtime: Containers can expose public UDP ports (eg, '-p 123/udp')
277
- + Runtime: Optionally specify an exact public port (eg. '-p 80:4500')
278
- + Registry: New image naming scheme inspired by Go packaging convention allows arbitrary combinations of registries
279
- + Builder: ENTRYPOINT instruction sets a default binary entry point to a container
280
- + Builder: VOLUME instruction marks a part of the container as persistent data
281
- * Builder: 'docker build' displays the full output of a build by default
282
- * Runtime: 'docker login' supports additional options
283
- - Runtime: Dont save a container's hostname when committing an image.
284
- - Registry: Fix issues when uploading images to a private registry
418
+
419
+#### Runtime
420
+
421
++ List all processes running inside a container with 'docker top'
422
++ Host directories can be mounted as volumes with 'docker run -v'
423
++ Containers can expose public UDP ports (eg, '-p 123/udp')
424
++ Optionally specify an exact public port (eg. '-p 80:4500')
425
+* 'docker login' supports additional options
426
+- Dont save a container`s hostname when committing an image.
427
+
428
+#### Registry
429
+
430
++ New image naming scheme inspired by Go packaging convention allows arbitrary combinations of registries
431
+- Fix issues when uploading images to a private registry
432
+
433
+#### Builder
434
+
435
++ ENTRYPOINT instruction sets a default binary entry point to a container
436
++ VOLUME instruction marks a part of the container as persistent data
437
+* 'docker build' displays the full output of a build by default
285 438
 
286 439
 ## 0.4.8 (2013-07-01)
287
- + Builder: New build operation ENTRYPOINT adds an executable entry point to the container.
288
- - Runtime: Fix a bug which caused 'docker run -d' to no longer print the container ID.
289
- - Tests: Fix issues in the test suite
440
+
441
++ Builder: New build operation ENTRYPOINT adds an executable entry point to the container.  - Runtime: Fix a bug which caused 'docker run -d' to no longer print the container ID.
442
+- Tests: Fix issues in the test suite
290 443
 
291 444
 ## 0.4.7 (2013-06-28)
292
- * Registry: easier push/pull to a custom registry
293
- * Remote API: the progress bar updates faster when downloading and uploading large files
294
- - Remote API: fix a bug in the optional unix socket transport
295
- * Runtime: improve detection of kernel version
296
- + Runtime: host directories can be mounted as volumes with 'docker run -b'
297
- - Runtime: fix an issue when only attaching to stdin
298
- * Runtime: use 'tar --numeric-owner' to avoid uid mismatch across multiple hosts
299
- * Hack: improve test suite and dev environment
300
- * Hack: remove dependency on unit tests on 'os/user'
301
- + Documentation: add terminology section
445
+
446
+#### Remote API
447
+
448
+* The progress bar updates faster when downloading and uploading large files
449
+- Fix a bug in the optional unix socket transport
450
+
451
+#### Runtime
452
+
453
+* Improve detection of kernel version
454
++ Host directories can be mounted as volumes with 'docker run -b'
455
+- fix an issue when only attaching to stdin
456
+* Use 'tar --numeric-owner' to avoid uid mismatch across multiple hosts
457
+
458
+#### Hack
459
+
460
+* Improve test suite and dev environment
461
+* Remove dependency on unit tests on 'os/user'
462
+
463
+#### Other
464
+
465
+* Registry: easier push/pull to a custom registry
466
++ Documentation: add terminology section
302 467
 
303 468
 ## 0.4.6 (2013-06-22)
304
- - Runtime: fix a bug which caused creation of empty images (and volumes) to crash.
469
+
470
+- Runtime: fix a bug which caused creation of empty images (and volumes) to crash.
305 471
 
306 472
 ## 0.4.5 (2013-06-21)
307
- + Builder: 'docker build git://URL' fetches and builds a remote git repository
308
- * Runtime: 'docker ps -s' optionally prints container size
309
- * Tests: Improved and simplified
310
- - Runtime: fix a regression introduced in 0.4.3 which caused the logs command to fail.
311
- - Builder: fix a regression when using ADD with single regular file.
473
+
474
++ Builder: 'docker build git://URL' fetches and builds a remote git repository
475
+* Runtime: 'docker ps -s' optionally prints container size
476
+* Tests: Improved and simplified
477
+- Runtime: fix a regression introduced in 0.4.3 which caused the logs command to fail.
478
+- Builder: fix a regression when using ADD with single regular file.
312 479
 
313 480
 ## 0.4.4 (2013-06-19)
314
- - Builder: fix a regression introduced in 0.4.3 which caused builds to fail on new clients.
481
+
482
+- Builder: fix a regression introduced in 0.4.3 which caused builds to fail on new clients.
315 483
 
316 484
 ## 0.4.3 (2013-06-19)
317
- + Builder: ADD of a local file will detect tar archives and unpack them
318
- * Runtime: Remove bsdtar dependency
319
- * Runtime: Add unix socket and multiple -H support
320
- * Runtime: Prevent rm of running containers
321
- * Runtime: Use go1.1 cookiejar
322
- * Builder: ADD improvements: use tar for copy + automatically unpack local archives
323
- * Builder: ADD uses tar/untar for copies instead of calling 'cp -ar'
324
- * Builder: nicer output for 'docker build'
325
- * Builder: fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented.
326
- * Client: HumanReadable ProgressBar sizes in pull
327
- * Client: Fix docker version's git commit output
328
- * API: Send all tags on History API call
329
- * API: Add tag lookup to history command. Fixes #882
330
- - Runtime: Fix issue detaching from running TTY container
331
- - Runtime: Forbid parralel push/pull for a single image/repo. Fixes #311
332
- - Runtime: Fix race condition within Run command when attaching.
333
- - Builder: fix a bug which caused builds to fail if ADD was the first command
334
- - Documentation: fix missing command in irc bouncer example
485
+
486
+#### Builder
487
+
488
++ ADD of a local file will detect tar archives and unpack them
489
+* ADD improvements: use tar for copy + automatically unpack local archives
490
+* ADD uses tar/untar for copies instead of calling 'cp -ar'
491
+* Fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented.
492
+- Fix a bug which caused builds to fail if ADD was the first command
493
+* Nicer output for 'docker build'
494
+
495
+#### Runtime
496
+
497
+* Remove bsdtar dependency
498
+* Add unix socket and multiple -H support
499
+* Prevent rm of running containers
500
+* Use go1.1 cookiejar
501
+- Fix issue detaching from running TTY container
502
+- Forbid parralel push/pull for a single image/repo. Fixes #311
503
+- Fix race condition within Run command when attaching.
504
+
505
+#### Client
506
+
507
+* HumanReadable ProgressBar sizes in pull
508
+* Fix docker version`s git commit output
509
+
510
+#### API
511
+
512
+* Send all tags on History API call
513
+* Add tag lookup to history command. Fixes #882
514
+
515
+#### Documentation
516
+
517
+- Fix missing command in irc bouncer example
335 518
 
336 519
 ## 0.4.2 (2013-06-17)
337
- - Packaging: Bumped version to work around an Ubuntu bug
520
+
521
+- Packaging: Bumped version to work around an Ubuntu bug
338 522
 
339 523
 ## 0.4.1 (2013-06-17)
340
- + Remote Api: Add flag to enable cross domain requests
341
- + Remote Api/Client: Add images and containers sizes in docker ps and docker images
342
- + Runtime: Configure dns configuration host-wide with 'docker -d -dns'
343
- + Runtime: Detect faulty DNS configuration and replace it with a public default
344
- + Runtime: allow docker run <name>:<id>
345
- + Runtime: you can now specify public port (ex: -p 80:4500)
346
- * Client: allow multiple params in inspect
347
- * Client: Print the container id before the hijack in `docker run`
348
- * Registry: add regexp check on repo's name
349
- * Registry: Move auth to the client
350
- * Runtime: improved image removal to garbage-collect unreferenced parents
351
- * Vagrantfile: Add the rest api port to vagrantfile's port_forward
352
- * Upgrade to Go 1.1
353
- - Builder: don't ignore last line in Dockerfile when it doesn't end with \n
354
- - Registry: Remove login check on pull
524
+
525
+#### Remote Api
526
+
527
++ Add flag to enable cross domain requests
528
++ Add images and containers sizes in docker ps and docker images
529
+
530
+#### Runtime
531
+
532
++ Configure dns configuration host-wide with 'docker -d -dns'
533
++ Detect faulty DNS configuration and replace it with a public default
534
++ Allow docker run <name>:<id>
535
++ You can now specify public port (ex: -p 80:4500)
536
+* Improved image removal to garbage-collect unreferenced parents
537
+
538
+#### Client
539
+
540
+* Allow multiple params in inspect
541
+* Print the container id before the hijack in `docker run`
542
+
543
+#### Registry
544
+
545
+* Add regexp check on repo`s name
546
+* Move auth to the client
547
+- Remove login check on pull
548
+
549
+#### Other
550
+
551
+* Vagrantfile: Add the rest api port to vagrantfile`s port_forward
552
+* Upgrade to Go 1.1
553
+- Builder: don`t ignore last line in Dockerfile when it doesn`t end with \n
355 554
 
356 555
 ## 0.4.0 (2013-06-03)
357
- + Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
358
- + Introducing Remote API: control Docker programmatically using a simple HTTP/json API
359
- * Runtime: various reliability and usability improvements
556
+
557
+#### Builder
558
+
559
++ Introducing Builder
560
++ 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
561
+
562
+#### Remote API
563
+
564
++ Introducing Remote API
565
++ control Docker programmatically using a simple HTTP/json API
566
+
567
+#### Runtime
568
+
569
+* Various reliability and usability improvements
360 570
 
361 571
 ## 0.3.4 (2013-05-30)
362
- + Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
363
- + Builder: 'docker build -t FOO' applies the tag FOO to the newly built container.
364
- + Runtime: interactive TTYs correctly handle window resize
365
- * Runtime: fix how configuration is merged between layers
366
- + Remote API: split stdout and stderr on 'docker run'
367
- + Remote API: optionally listen on a different IP and port (use at your own risk)
368
- * Documentation: improved install instructions.
572
+
573
+#### Builder
574
+
575
++ 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile
576
++ 'docker build -t FOO' applies the tag FOO to the newly built container.
577
+
578
+#### Runtime
579
+
580
++ Interactive TTYs correctly handle window resize
581
+* Fix how configuration is merged between layers
582
+
583
+#### Remote API
584
+
585
++ Split stdout and stderr on 'docker run'
586
++ Optionally listen on a different IP and port (use at your own risk)
587
+
588
+#### Documentation
589
+
590
+* Improved install instructions.
369 591
 
370 592
 ## 0.3.3 (2013-05-23)
371
- - Registry: Fix push regression
372
- - Various bugfixes
593
+
594
+- Registry: Fix push regression
595
+- Various bugfixes
373 596
 
374 597
 ## 0.3.2 (2013-05-09)
375
- * Runtime: Store the actual archive on commit
376
- * Registry: Improve the checksum process
377
- * Registry: Use the size to have a good progress bar while pushing
378
- * Registry: Use the actual archive if it exists in order to speed up the push
379
- - Registry: Fix error 400 on push
598
+
599
+#### Registry
600
+
601
+* Improve the checksum process
602
+* Use the size to have a good progress bar while pushing
603
+* Use the actual archive if it exists in order to speed up the push
604
+- Fix error 400 on push
605
+
606
+#### Runtime
607
+
608
+* Store the actual archive on commit
380 609
 
381 610
 ## 0.3.1 (2013-05-08)
382
- + Builder: Implement the autorun capability within docker builder
383
- + Builder: Add caching to docker builder
384
- + Builder: Add support for docker builder with native API as top level command
385
- + Runtime: Add go version to debug infos
386
- + Builder: Implement ENV within docker builder
387
- + Registry: Add docker search top level command in order to search a repository
388
- + Images: output graph of images to dot (graphviz)
389
- + Documentation: new introduction and high-level overview
390
- + Documentation: Add the documentation for docker builder
391
- + Website: new high-level overview
392
- - Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc
393
- - Images: fix ByParent function
394
- - Builder: Check the command existance prior create and add Unit tests for the case
395
- - Registry: Fix pull for official images with specific tag
396
- - Registry: Fix issue when login in with a different user and trying to push
397
- - Documentation: CSS fix for docker documentation to make REST API docs look better.
398
- - Documentation: Fixed CouchDB example page header mistake
399
- - Documentation: fixed README formatting
400
- * Registry: Improve checksum - async calculation
401
- * Runtime: kernel version - don't show the dash if flavor is empty
402
- * Documentation: updated www.docker.io website.
403
- * Builder: use any whitespaces instead of tabs
404
- * Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker
611
+
612
+#### Builder
613
+
614
++ Implement the autorun capability within docker builder
615
++ Add caching to docker builder
616
++ Add support for docker builder with native API as top level command
617
++ Implement ENV within docker builder
618
+- Check the command existance prior create and add Unit tests for the case
619
+* use any whitespaces instead of tabs
620
+
621
+#### Runtime
622
+
623
++ Add go version to debug infos
624
+* Kernel version - don`t show the dash if flavor is empty
625
+
626
+#### Registry
627
+
628
++ Add docker search top level command in order to search a repository
629
+- Fix pull for official images with specific tag
630
+- Fix issue when login in with a different user and trying to push
631
+* Improve checksum - async calculation
632
+
633
+#### Images
634
+
635
++ Output graph of images to dot (graphviz)
636
+- Fix ByParent function
637
+
638
+#### Documentation
639
+
640
++ New introduction and high-level overview
641
++ Add the documentation for docker builder
642
+- CSS fix for docker documentation to make REST API docs look better.
643
+- Fix CouchDB example page header mistake
644
+- Fix README formatting
645
+* Update www.docker.io website.
646
+
647
+#### Other
648
+
649
++ Website: new high-level overview
650
+- Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc
651
+* Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker
405 652
 
406 653
 ## 0.3.0 (2013-05-06)
407
- + Registry: Implement the new registry
408
- + Documentation: new example: sharing data between 2 couchdb databases
409
- - Runtime: Fix the command existance check
410
- - Runtime: strings.Split may return an empty string on no match
411
- - Runtime: Fix an index out of range crash if cgroup memory is not
412
- * Documentation: Various improvments
413
- * Vagrant: Use only one deb line in /etc/apt
654
+
655
+#### Runtime
656
+
657
+- Fix the command existance check
658
+- strings.Split may return an empty string on no match
659
+- Fix an index out of range crash if cgroup memory is not
660
+
661
+#### Documentation
662
+
663
+* Various improvments
664
++ New example: sharing data between 2 couchdb databases
665
+
666
+#### Other
667
+
668
+* Vagrant: Use only one deb line in /etc/apt
669
++ Registry: Implement the new registry
414 670
 
415 671
 ## 0.2.2 (2013-05-03)
416
- + Support for data volumes ('docker run -v=PATH')
417
- + Share data volumes between containers ('docker run -volumes-from')
418
- + Improved documentation
419
- * Upgrade to Go 1.0.3
420
- * Various upgrades to the dev environment for contributors
672
+
673
++ Support for data volumes ('docker run -v=PATH')
674
++ Share data volumes between containers ('docker run -volumes-from')
675
++ Improved documentation
676
+* Upgrade to Go 1.0.3
677
+* Various upgrades to the dev environment for contributors
421 678
 
422 679
 ## 0.2.1 (2013-05-01)
423
- + 'docker commit -run' bundles a layer with default runtime options: command, ports etc.
424
- * Improve install process on Vagrant
425
- + New Dockerfile operation: "maintainer"
426
- + New Dockerfile operation: "expose"
427
- + New Dockerfile operation: "cmd"
428
- + Contrib script to build a Debian base layer
429
- + 'docker -d -r': restart crashed containers at daemon startup
430
- * Runtime: improve test coverage
680
+
681
++ 'docker commit -run' bundles a layer with default runtime options: command, ports etc.
682
+* Improve install process on Vagrant
683
++ New Dockerfile operation: "maintainer"
684
++ New Dockerfile operation: "expose"
685
++ New Dockerfile operation: "cmd"
686
++ Contrib script to build a Debian base layer
687
++ 'docker -d -r': restart crashed containers at daemon startup
688
+* Runtime: improve test coverage
431 689
 
432 690
 ## 0.2.0 (2013-04-23)
433
- - Runtime: ghost containers can be killed and waited for
434
- * Documentation: update install intructions
435
- - Packaging: fix Vagrantfile
436
- - Development: automate releasing binaries and ubuntu packages
437
- + Add a changelog
438
- - Various bugfixes
691
+
692
+- Runtime: ghost containers can be killed and waited for
693
+* Documentation: update install intructions
694
+- Packaging: fix Vagrantfile
695
+- Development: automate releasing binaries and ubuntu packages
696
++ Add a changelog
697
+- Various bugfixes
439 698
 
440 699
 ## 0.1.8 (2013-04-22)
441
- - Dynamically detect cgroup capabilities
442
- - Issue stability warning on kernels <3.8
443
- - 'docker push' buffers on disk instead of memory
444
- - Fix 'docker diff' for removed files
445
- - Fix 'docker stop' for ghost containers
446
- - Fix handling of pidfile
447
- - Various bugfixes and stability improvements
700
+
701
+- Dynamically detect cgroup capabilities
702
+- Issue stability warning on kernels <3.8
703
+- 'docker push' buffers on disk instead of memory
704
+- Fix 'docker diff' for removed files
705
+- Fix 'docker stop' for ghost containers
706
+- Fix handling of pidfile
707
+- Various bugfixes and stability improvements
448 708
 
449 709
 ## 0.1.7 (2013-04-18)
450
- - Container ports are available on localhost
451
- - 'docker ps' shows allocated TCP ports
452
- - Contributors can run 'make hack' to start a continuous integration VM
453
- - Streamline ubuntu packaging & uploading
454
- - Various bugfixes and stability improvements
710
+
711
+- Container ports are available on localhost
712
+- 'docker ps' shows allocated TCP ports
713
+- Contributors can run 'make hack' to start a continuous integration VM
714
+- Streamline ubuntu packaging & uploading
715
+- Various bugfixes and stability improvements
455 716
 
456 717
 ## 0.1.6 (2013-04-17)
457
- - Record the author an image with 'docker commit -author'
718
+
719
+- Record the author an image with 'docker commit -author'
458 720
 
459 721
 ## 0.1.5 (2013-04-17)
460
- - Disable standalone mode
461
- - Use a custom DNS resolver with 'docker -d -dns'
462
- - Detect ghost containers
463
- - Improve diagnosis of missing system capabilities
464
- - Allow disabling memory limits at compile time
465
- - Add debian packaging
466
- - Documentation: installing on Arch Linux
467
- - Documentation: running Redis on docker
468
- - Fixed lxc 0.9 compatibility
469
- - Automatically load aufs module
470
- - Various bugfixes and stability improvements
722
+
723
+- Disable standalone mode
724
+- Use a custom DNS resolver with 'docker -d -dns'
725
+- Detect ghost containers
726
+- Improve diagnosis of missing system capabilities
727
+- Allow disabling memory limits at compile time
728
+- Add debian packaging
729
+- Documentation: installing on Arch Linux
730
+- Documentation: running Redis on docker
731
+- Fixed lxc 0.9 compatibility
732
+- Automatically load aufs module
733
+- Various bugfixes and stability improvements
471 734
 
472 735
 ## 0.1.4 (2013-04-09)
473
- - Full support for TTY emulation
474
- - Detach from a TTY session with the escape sequence `C-p C-q`
475
- - Various bugfixes and stability improvements
476
- - Minor UI improvements
477
- - Automatically create our own bridge interface 'docker0'
736
+
737
+- Full support for TTY emulation
738
+- Detach from a TTY session with the escape sequence `C-p C-q`
739
+- Various bugfixes and stability improvements
740
+- Minor UI improvements
741
+- Automatically create our own bridge interface 'docker0'
478 742
 
479 743
 ## 0.1.3 (2013-04-04)
480
- - Choose TCP frontend port with '-p :PORT'
481
- - Layer format is versioned
482
- - Major reliability improvements to the process manager
483
- - Various bugfixes and stability improvements
744
+
745
+- Choose TCP frontend port with '-p :PORT'
746
+- Layer format is versioned
747
+- Major reliability improvements to the process manager
748
+- Various bugfixes and stability improvements
484 749
 
485 750
 ## 0.1.2 (2013-04-03)
486
- - Set container hostname with 'docker run -h'
487
- - Selective attach at run with 'docker run -a [stdin[,stdout[,stderr]]]'
488
- - Various bugfixes and stability improvements
489
- - UI polish
490
- - Progress bar on push/pull
491
- - Use XZ compression by default
492
- - Make IP allocator lazy
751
+
752
+- Set container hostname with 'docker run -h'
753
+- Selective attach at run with 'docker run -a [stdin[,stdout[,stderr]]]'
754
+- Various bugfixes and stability improvements
755
+- UI polish
756
+- Progress bar on push/pull
757
+- Use XZ compression by default
758
+- Make IP allocator lazy
493 759
 
494 760
 ## 0.1.1 (2013-03-31)
495
- - Display shorthand IDs for convenience
496
- - Stabilize process management
497
- - Layers can include a commit message
498
- - Simplified 'docker attach'
499
- - Fixed support for re-attaching
500
- - Various bugfixes and stability improvements
501
- - Auto-download at run
502
- - Auto-login on push
503
- - Beefed up documentation
761
+
762
+- Display shorthand IDs for convenience
763
+- Stabilize process management
764
+- Layers can include a commit message
765
+- Simplified 'docker attach'
766
+- Fixed support for re-attaching
767
+- Various bugfixes and stability improvements
768
+- Auto-download at run
769
+- Auto-login on push
770
+- Beefed up documentation
504 771
 
505 772
 ## 0.1.0 (2013-03-23)
506
- - First release
507
- - Implement registry in order to push/pull images
508
- - TCP port allocation
509
- - Fix termcaps on Linux
510
- - Add documentation
511
- - Add Vagrant support with Vagrantfile
512
- - Add unit tests
513
- - Add repository/tags to ease image management
514
- - Improve the layer implementation
773
+
774
+Initial public release
775
+
776
+- Implement registry in order to push/pull images
777
+- TCP port allocation
778
+- Fix termcaps on Linux
779
+- Add documentation
780
+- Add Vagrant support with Vagrantfile
781
+- Add unit tests
782
+- Add repository/tags to ease image management
783
+- Improve the layer implementation
... ...
@@ -4,24 +4,24 @@
4 4
 #
5 5
 # # Assemble the full dev environment. This is slow the first time.
6 6
 # docker build -t docker .
7
-# # Apparmor messes with privileged mode: disable it
8
-# /etc/init.d/apparmor stop ; /etc/init.d/apparmor teardown
9 7
 #
10 8
 # # Mount your source in an interactive container for quick testing:
11
-# docker run -v `pwd`:/go/src/github.com/dotcloud/docker -privileged -lxc-conf=lxc.aa_profile=unconfined -i -t docker bash
12
-#
9
+# docker run -v `pwd`:/go/src/github.com/dotcloud/docker -privileged -i -t docker bash
13 10
 #
14 11
 # # Run the test suite:
15
-# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh test
12
+# docker run -privileged docker hack/make.sh test
16 13
 #
17 14
 # # Publish a release:
18
-# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined \
15
+# docker run -privileged \
19 16
 #  -e AWS_S3_BUCKET=baz \
20 17
 #  -e AWS_ACCESS_KEY=foo \
21 18
 #  -e AWS_SECRET_KEY=bar \
22 19
 #  -e GPG_PASSPHRASE=gloubiboulga \
23 20
 #  docker hack/release.sh
24 21
 #
22
+# Note: Apparmor used to mess with privileged mode, but this is no longer
23
+# the case. Therefore, you don't have to disable it anymore.
24
+#
25 25
 
26 26
 docker-version 0.6.1
27 27
 from	ubuntu:12.04
... ...
@@ -36,7 +36,7 @@ run	apt-get install -y -q mercurial
36 36
 run apt-get install -y -q build-essential libsqlite3-dev
37 37
 
38 38
 # Install Go
39
-run	curl -s https://go.googlecode.com/files/go1.2rc2.src.tar.gz | tar -v -C /usr/local -xz
39
+run	curl -s https://go.googlecode.com/files/go1.2rc3.src.tar.gz | tar -v -C /usr/local -xz
40 40
 env	PATH	/usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
41 41
 env	GOPATH	/go:/go/src/github.com/dotcloud/docker/vendor
42 42
 run cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
... ...
@@ -1 +1 @@
1
-0.6.5
1
+0.6.6-dev
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"encoding/base64"
6 6
 	"encoding/json"
7 7
 	"fmt"
8
+	"github.com/dotcloud/docker/archive"
8 9
 	"github.com/dotcloud/docker/auth"
9 10
 	"github.com/dotcloud/docker/utils"
10 11
 	"github.com/gorilla/mux"
... ...
@@ -235,6 +236,7 @@ func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
235 235
 	}
236 236
 	w.Header().Set("Content-Type", "application/json")
237 237
 	wf := utils.NewWriteFlusher(w)
238
+	wf.Flush()
238 239
 	if since != 0 {
239 240
 		// If since, send previous events that happened after the timestamp
240 241
 		for _, event := range srv.events {
... ...
@@ -905,7 +907,7 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
905 905
 			return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
906 906
 		}
907 907
 
908
-		c, err := Tar(root, Bzip2)
908
+		c, err := archive.Tar(root, archive.Bzip2)
909 909
 		if err != nil {
910 910
 			return err
911 911
 		}
... ...
@@ -5,6 +5,7 @@ type APIHistory struct {
5 5
 	Tags      []string `json:",omitempty"`
6 6
 	Created   int64
7 7
 	CreatedBy string `json:",omitempty"`
8
+	Size      int64
8 9
 }
9 10
 
10 11
 type APIImages struct {
... ...
@@ -77,11 +78,6 @@ type APIContainersOld struct {
77 77
 	SizeRootFs int64
78 78
 }
79 79
 
80
-type APISearch struct {
81
-	Name        string
82
-	Description string
83
-}
84
-
85 80
 type APIID struct {
86 81
 	ID string `json:"Id"`
87 82
 }
... ...
@@ -499,8 +499,7 @@ func TestGetContainersTop(t *testing.T) {
499 499
 		container.WaitTimeout(2 * time.Second)
500 500
 	}()
501 501
 
502
-	hostConfig := &HostConfig{}
503
-	if err := container.Start(hostConfig); err != nil {
502
+	if err := container.Start(); err != nil {
504 503
 		t.Fatal(err)
505 504
 	}
506 505
 
... ...
@@ -704,8 +703,7 @@ func TestPostContainersKill(t *testing.T) {
704 704
 	}
705 705
 	defer runtime.Destroy(container)
706 706
 
707
-	hostConfig := &HostConfig{}
708
-	if err := container.Start(hostConfig); err != nil {
707
+	if err := container.Start(); err != nil {
709 708
 		t.Fatal(err)
710 709
 	}
711 710
 
... ...
@@ -747,8 +745,7 @@ func TestPostContainersRestart(t *testing.T) {
747 747
 	}
748 748
 	defer runtime.Destroy(container)
749 749
 
750
-	hostConfig := &HostConfig{}
751
-	if err := container.Start(hostConfig); err != nil {
750
+	if err := container.Start(); err != nil {
752 751
 		t.Fatal(err)
753 752
 	}
754 753
 
... ...
@@ -855,8 +852,7 @@ func TestPostContainersStop(t *testing.T) {
855 855
 	}
856 856
 	defer runtime.Destroy(container)
857 857
 
858
-	hostConfig := &HostConfig{}
859
-	if err := container.Start(hostConfig); err != nil {
858
+	if err := container.Start(); err != nil {
860 859
 		t.Fatal(err)
861 860
 	}
862 861
 
... ...
@@ -903,8 +899,7 @@ func TestPostContainersWait(t *testing.T) {
903 903
 	}
904 904
 	defer runtime.Destroy(container)
905 905
 
906
-	hostConfig := &HostConfig{}
907
-	if err := container.Start(hostConfig); err != nil {
906
+	if err := container.Start(); err != nil {
908 907
 		t.Fatal(err)
909 908
 	}
910 909
 
... ...
@@ -947,8 +942,7 @@ func TestPostContainersAttach(t *testing.T) {
947 947
 	defer runtime.Destroy(container)
948 948
 
949 949
 	// Start the process
950
-	hostConfig := &HostConfig{}
951
-	if err := container.Start(hostConfig); err != nil {
950
+	if err := container.Start(); err != nil {
952 951
 		t.Fatal(err)
953 952
 	}
954 953
 
... ...
@@ -1037,8 +1031,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
1037 1037
 	defer runtime.Destroy(container)
1038 1038
 
1039 1039
 	// Start the process
1040
-	hostConfig := &HostConfig{}
1041
-	if err := container.Start(hostConfig); err != nil {
1040
+	if err := container.Start(); err != nil {
1042 1041
 		t.Fatal(err)
1043 1042
 	}
1044 1043
 
1045 1044
deleted file mode 100644
... ...
@@ -1,302 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"archive/tar"
5
-	"bytes"
6
-	"fmt"
7
-	"github.com/dotcloud/docker/utils"
8
-	"io"
9
-	"io/ioutil"
10
-	"os"
11
-	"os/exec"
12
-	"path"
13
-	"path/filepath"
14
-)
15
-
16
-type Archive io.Reader
17
-
18
-type Compression uint32
19
-
20
-const (
21
-	Uncompressed Compression = iota
22
-	Bzip2
23
-	Gzip
24
-	Xz
25
-)
26
-
27
-func DetectCompression(source []byte) Compression {
28
-	sourceLen := len(source)
29
-	for compression, m := range map[Compression][]byte{
30
-		Bzip2: {0x42, 0x5A, 0x68},
31
-		Gzip:  {0x1F, 0x8B, 0x08},
32
-		Xz:    {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00},
33
-	} {
34
-		fail := false
35
-		if len(m) > sourceLen {
36
-			utils.Debugf("Len too short")
37
-			continue
38
-		}
39
-		i := 0
40
-		for _, b := range m {
41
-			if b != source[i] {
42
-				fail = true
43
-				break
44
-			}
45
-			i++
46
-		}
47
-		if !fail {
48
-			return compression
49
-		}
50
-	}
51
-	return Uncompressed
52
-}
53
-
54
-func (compression *Compression) Flag() string {
55
-	switch *compression {
56
-	case Bzip2:
57
-		return "j"
58
-	case Gzip:
59
-		return "z"
60
-	case Xz:
61
-		return "J"
62
-	}
63
-	return ""
64
-}
65
-
66
-func (compression *Compression) Extension() string {
67
-	switch *compression {
68
-	case Uncompressed:
69
-		return "tar"
70
-	case Bzip2:
71
-		return "tar.bz2"
72
-	case Gzip:
73
-		return "tar.gz"
74
-	case Xz:
75
-		return "tar.xz"
76
-	}
77
-	return ""
78
-}
79
-
80
-// Tar creates an archive from the directory at `path`, and returns it as a
81
-// stream of bytes.
82
-func Tar(path string, compression Compression) (io.Reader, error) {
83
-	return TarFilter(path, compression, nil)
84
-}
85
-
86
-// Tar creates an archive from the directory at `path`, only including files whose relative
87
-// paths are included in `filter`. If `filter` is nil, then all files are included.
88
-func TarFilter(path string, compression Compression, filter []string) (io.Reader, error) {
89
-	args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path}
90
-	if filter == nil {
91
-		filter = []string{"."}
92
-	}
93
-	for _, f := range filter {
94
-		args = append(args, "-c"+compression.Flag(), f)
95
-	}
96
-	return CmdStream(exec.Command(args[0], args[1:]...))
97
-}
98
-
99
-// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
100
-// and unpacks it into the directory at `path`.
101
-// The archive may be compressed with one of the following algorithms:
102
-//  identity (uncompressed), gzip, bzip2, xz.
103
-// FIXME: specify behavior when target path exists vs. doesn't exist.
104
-func Untar(archive io.Reader, path string) error {
105
-	if archive == nil {
106
-		return fmt.Errorf("Empty archive")
107
-	}
108
-
109
-	buf := make([]byte, 10)
110
-	totalN := 0
111
-	for totalN < 10 {
112
-		if n, err := archive.Read(buf[totalN:]); err != nil {
113
-			if err == io.EOF {
114
-				return fmt.Errorf("Tarball too short")
115
-			}
116
-			return err
117
-		} else {
118
-			totalN += n
119
-			utils.Debugf("[tar autodetect] n: %d", n)
120
-		}
121
-	}
122
-	compression := DetectCompression(buf)
123
-
124
-	utils.Debugf("Archive compression detected: %s", compression.Extension())
125
-
126
-	cmd := exec.Command("tar", "--numeric-owner", "-f", "-", "-C", path, "-x"+compression.Flag())
127
-	cmd.Stdin = io.MultiReader(bytes.NewReader(buf), archive)
128
-	// Hardcode locale environment for predictable outcome regardless of host configuration.
129
-	//   (see https://github.com/dotcloud/docker/issues/355)
130
-	cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
131
-	output, err := cmd.CombinedOutput()
132
-	if err != nil {
133
-		return fmt.Errorf("%s: %s", err, output)
134
-	}
135
-	return nil
136
-}
137
-
138
-// TarUntar is a convenience function which calls Tar and Untar, with
139
-// the output of one piped into the other. If either Tar or Untar fails,
140
-// TarUntar aborts and returns the error.
141
-func TarUntar(src string, filter []string, dst string) error {
142
-	utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
143
-	archive, err := TarFilter(src, Uncompressed, filter)
144
-	if err != nil {
145
-		return err
146
-	}
147
-	return Untar(archive, dst)
148
-}
149
-
150
-// UntarPath is a convenience function which looks for an archive
151
-// at filesystem path `src`, and unpacks it at `dst`.
152
-func UntarPath(src, dst string) error {
153
-	if archive, err := os.Open(src); err != nil {
154
-		return err
155
-	} else if err := Untar(archive, dst); err != nil {
156
-		return err
157
-	}
158
-	return nil
159
-}
160
-
161
-// CopyWithTar creates a tar archive of filesystem path `src`, and
162
-// unpacks it at filesystem path `dst`.
163
-// The archive is streamed directly with fixed buffering and no
164
-// intermediary disk IO.
165
-//
166
-func CopyWithTar(src, dst string) error {
167
-	srcSt, err := os.Stat(src)
168
-	if err != nil {
169
-		return err
170
-	}
171
-	if !srcSt.IsDir() {
172
-		return CopyFileWithTar(src, dst)
173
-	}
174
-	// Create dst, copy src's content into it
175
-	utils.Debugf("Creating dest directory: %s", dst)
176
-	if err := os.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) {
177
-		return err
178
-	}
179
-	utils.Debugf("Calling TarUntar(%s, %s)", src, dst)
180
-	return TarUntar(src, nil, dst)
181
-}
182
-
183
-// CopyFileWithTar emulates the behavior of the 'cp' command-line
184
-// for a single file. It copies a regular file from path `src` to
185
-// path `dst`, and preserves all its metadata.
186
-//
187
-// If `dst` ends with a trailing slash '/', the final destination path
188
-// will be `dst/base(src)`.
189
-func CopyFileWithTar(src, dst string) error {
190
-	utils.Debugf("CopyFileWithTar(%s, %s)", src, dst)
191
-	srcSt, err := os.Stat(src)
192
-	if err != nil {
193
-		return err
194
-	}
195
-	if srcSt.IsDir() {
196
-		return fmt.Errorf("Can't copy a directory")
197
-	}
198
-	// Clean up the trailing /
199
-	if dst[len(dst)-1] == '/' {
200
-		dst = path.Join(dst, filepath.Base(src))
201
-	}
202
-	// Create the holding directory if necessary
203
-	if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
204
-		return err
205
-	}
206
-	buf := new(bytes.Buffer)
207
-	tw := tar.NewWriter(buf)
208
-	hdr, err := tar.FileInfoHeader(srcSt, "")
209
-	if err != nil {
210
-		return err
211
-	}
212
-	hdr.Name = filepath.Base(dst)
213
-	if err := tw.WriteHeader(hdr); err != nil {
214
-		return err
215
-	}
216
-	srcF, err := os.Open(src)
217
-	if err != nil {
218
-		return err
219
-	}
220
-	if _, err := io.Copy(tw, srcF); err != nil {
221
-		return err
222
-	}
223
-	tw.Close()
224
-	return Untar(buf, filepath.Dir(dst))
225
-}
226
-
227
-// CmdStream executes a command, and returns its stdout as a stream.
228
-// If the command fails to run or doesn't complete successfully, an error
229
-// will be returned, including anything written on stderr.
230
-func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
231
-	stdout, err := cmd.StdoutPipe()
232
-	if err != nil {
233
-		return nil, err
234
-	}
235
-	stderr, err := cmd.StderrPipe()
236
-	if err != nil {
237
-		return nil, err
238
-	}
239
-	pipeR, pipeW := io.Pipe()
240
-	errChan := make(chan []byte)
241
-	// Collect stderr, we will use it in case of an error
242
-	go func() {
243
-		errText, e := ioutil.ReadAll(stderr)
244
-		if e != nil {
245
-			errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
246
-		}
247
-		errChan <- errText
248
-	}()
249
-	// Copy stdout to the returned pipe
250
-	go func() {
251
-		_, err := io.Copy(pipeW, stdout)
252
-		if err != nil {
253
-			pipeW.CloseWithError(err)
254
-		}
255
-		errText := <-errChan
256
-		if err := cmd.Wait(); err != nil {
257
-			pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errText))
258
-		} else {
259
-			pipeW.Close()
260
-		}
261
-	}()
262
-	// Run the command and return the pipe
263
-	if err := cmd.Start(); err != nil {
264
-		return nil, err
265
-	}
266
-	return pipeR, nil
267
-}
268
-
269
-// NewTempArchive reads the content of src into a temporary file, and returns the contents
270
-// of that file as an archive. The archive can only be read once - as soon as reading completes,
271
-// the file will be deleted.
272
-func NewTempArchive(src Archive, dir string) (*TempArchive, error) {
273
-	f, err := ioutil.TempFile(dir, "")
274
-	if err != nil {
275
-		return nil, err
276
-	}
277
-	if _, err := io.Copy(f, src); err != nil {
278
-		return nil, err
279
-	}
280
-	if _, err := f.Seek(0, 0); err != nil {
281
-		return nil, err
282
-	}
283
-	st, err := f.Stat()
284
-	if err != nil {
285
-		return nil, err
286
-	}
287
-	size := st.Size()
288
-	return &TempArchive{f, size}, nil
289
-}
290
-
291
-type TempArchive struct {
292
-	*os.File
293
-	Size int64 // Pre-computed from Stat().Size() as a convenience
294
-}
295
-
296
-func (archive *TempArchive) Read(data []byte) (int, error) {
297
-	n, err := archive.File.Read(data)
298
-	if err != nil {
299
-		os.Remove(archive.File.Name())
300
-	}
301
-	return n, err
302
-}
303 1
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
0 1
new file mode 100644
... ...
@@ -0,0 +1,302 @@
0
+package archive
1
+
2
+import (
3
+	"archive/tar"
4
+	"bytes"
5
+	"fmt"
6
+	"github.com/dotcloud/docker/utils"
7
+	"io"
8
+	"io/ioutil"
9
+	"os"
10
+	"os/exec"
11
+	"path"
12
+	"path/filepath"
13
+)
14
+
15
+type Archive io.Reader
16
+
17
+type Compression uint32
18
+
19
+const (
20
+	Uncompressed Compression = iota
21
+	Bzip2
22
+	Gzip
23
+	Xz
24
+)
25
+
26
+func DetectCompression(source []byte) Compression {
27
+	sourceLen := len(source)
28
+	for compression, m := range map[Compression][]byte{
29
+		Bzip2: {0x42, 0x5A, 0x68},
30
+		Gzip:  {0x1F, 0x8B, 0x08},
31
+		Xz:    {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00},
32
+	} {
33
+		fail := false
34
+		if len(m) > sourceLen {
35
+			utils.Debugf("Len too short")
36
+			continue
37
+		}
38
+		i := 0
39
+		for _, b := range m {
40
+			if b != source[i] {
41
+				fail = true
42
+				break
43
+			}
44
+			i++
45
+		}
46
+		if !fail {
47
+			return compression
48
+		}
49
+	}
50
+	return Uncompressed
51
+}
52
+
53
+func (compression *Compression) Flag() string {
54
+	switch *compression {
55
+	case Bzip2:
56
+		return "j"
57
+	case Gzip:
58
+		return "z"
59
+	case Xz:
60
+		return "J"
61
+	}
62
+	return ""
63
+}
64
+
65
+func (compression *Compression) Extension() string {
66
+	switch *compression {
67
+	case Uncompressed:
68
+		return "tar"
69
+	case Bzip2:
70
+		return "tar.bz2"
71
+	case Gzip:
72
+		return "tar.gz"
73
+	case Xz:
74
+		return "tar.xz"
75
+	}
76
+	return ""
77
+}
78
+
79
+// Tar creates an archive from the directory at `path`, and returns it as a
80
+// stream of bytes.
81
+func Tar(path string, compression Compression) (io.Reader, error) {
82
+	return TarFilter(path, compression, nil)
83
+}
84
+
85
+// Tar creates an archive from the directory at `path`, only including files whose relative
86
+// paths are included in `filter`. If `filter` is nil, then all files are included.
87
+func TarFilter(path string, compression Compression, filter []string) (io.Reader, error) {
88
+	args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path}
89
+	if filter == nil {
90
+		filter = []string{"."}
91
+	}
92
+	for _, f := range filter {
93
+		args = append(args, "-c"+compression.Flag(), f)
94
+	}
95
+	return CmdStream(exec.Command(args[0], args[1:]...))
96
+}
97
+
98
+// Untar reads a stream of bytes from `archive`, parses it as a tar archive,
99
+// and unpacks it into the directory at `path`.
100
+// The archive may be compressed with one of the following algorithms:
101
+//  identity (uncompressed), gzip, bzip2, xz.
102
+// FIXME: specify behavior when target path exists vs. doesn't exist.
103
+func Untar(archive io.Reader, path string) error {
104
+	if archive == nil {
105
+		return fmt.Errorf("Empty archive")
106
+	}
107
+
108
+	buf := make([]byte, 10)
109
+	totalN := 0
110
+	for totalN < 10 {
111
+		if n, err := archive.Read(buf[totalN:]); err != nil {
112
+			if err == io.EOF {
113
+				return fmt.Errorf("Tarball too short")
114
+			}
115
+			return err
116
+		} else {
117
+			totalN += n
118
+			utils.Debugf("[tar autodetect] n: %d", n)
119
+		}
120
+	}
121
+	compression := DetectCompression(buf)
122
+
123
+	utils.Debugf("Archive compression detected: %s", compression.Extension())
124
+
125
+	cmd := exec.Command("tar", "--numeric-owner", "-f", "-", "-C", path, "-x"+compression.Flag())
126
+	cmd.Stdin = io.MultiReader(bytes.NewReader(buf), archive)
127
+	// Hardcode locale environment for predictable outcome regardless of host configuration.
128
+	//   (see https://github.com/dotcloud/docker/issues/355)
129
+	cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"}
130
+	output, err := cmd.CombinedOutput()
131
+	if err != nil {
132
+		return fmt.Errorf("%s: %s", err, output)
133
+	}
134
+	return nil
135
+}
136
+
137
+// TarUntar is a convenience function which calls Tar and Untar, with
138
+// the output of one piped into the other. If either Tar or Untar fails,
139
+// TarUntar aborts and returns the error.
140
+func TarUntar(src string, filter []string, dst string) error {
141
+	utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
142
+	archive, err := TarFilter(src, Uncompressed, filter)
143
+	if err != nil {
144
+		return err
145
+	}
146
+	return Untar(archive, dst)
147
+}
148
+
149
+// UntarPath is a convenience function which looks for an archive
150
+// at filesystem path `src`, and unpacks it at `dst`.
151
+func UntarPath(src, dst string) error {
152
+	if archive, err := os.Open(src); err != nil {
153
+		return err
154
+	} else if err := Untar(archive, dst); err != nil {
155
+		return err
156
+	}
157
+	return nil
158
+}
159
+
160
+// CopyWithTar creates a tar archive of filesystem path `src`, and
161
+// unpacks it at filesystem path `dst`.
162
+// The archive is streamed directly with fixed buffering and no
163
+// intermediary disk IO.
164
+//
165
+func CopyWithTar(src, dst string) error {
166
+	srcSt, err := os.Stat(src)
167
+	if err != nil {
168
+		return err
169
+	}
170
+	if !srcSt.IsDir() {
171
+		return CopyFileWithTar(src, dst)
172
+	}
173
+	// Create dst, copy src's content into it
174
+	utils.Debugf("Creating dest directory: %s", dst)
175
+	if err := os.MkdirAll(dst, 0755); err != nil && !os.IsExist(err) {
176
+		return err
177
+	}
178
+	utils.Debugf("Calling TarUntar(%s, %s)", src, dst)
179
+	return TarUntar(src, nil, dst)
180
+}
181
+
182
+// CopyFileWithTar emulates the behavior of the 'cp' command-line
183
+// for a single file. It copies a regular file from path `src` to
184
+// path `dst`, and preserves all its metadata.
185
+//
186
+// If `dst` ends with a trailing slash '/', the final destination path
187
+// will be `dst/base(src)`.
188
+func CopyFileWithTar(src, dst string) error {
189
+	utils.Debugf("CopyFileWithTar(%s, %s)", src, dst)
190
+	srcSt, err := os.Stat(src)
191
+	if err != nil {
192
+		return err
193
+	}
194
+	if srcSt.IsDir() {
195
+		return fmt.Errorf("Can't copy a directory")
196
+	}
197
+	// Clean up the trailing /
198
+	if dst[len(dst)-1] == '/' {
199
+		dst = path.Join(dst, filepath.Base(src))
200
+	}
201
+	// Create the holding directory if necessary
202
+	if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
203
+		return err
204
+	}
205
+	buf := new(bytes.Buffer)
206
+	tw := tar.NewWriter(buf)
207
+	hdr, err := tar.FileInfoHeader(srcSt, "")
208
+	if err != nil {
209
+		return err
210
+	}
211
+	hdr.Name = filepath.Base(dst)
212
+	if err := tw.WriteHeader(hdr); err != nil {
213
+		return err
214
+	}
215
+	srcF, err := os.Open(src)
216
+	if err != nil {
217
+		return err
218
+	}
219
+	if _, err := io.Copy(tw, srcF); err != nil {
220
+		return err
221
+	}
222
+	tw.Close()
223
+	return Untar(buf, filepath.Dir(dst))
224
+}
225
+
226
+// CmdStream executes a command, and returns its stdout as a stream.
227
+// If the command fails to run or doesn't complete successfully, an error
228
+// will be returned, including anything written on stderr.
229
+func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
230
+	stdout, err := cmd.StdoutPipe()
231
+	if err != nil {
232
+		return nil, err
233
+	}
234
+	stderr, err := cmd.StderrPipe()
235
+	if err != nil {
236
+		return nil, err
237
+	}
238
+	pipeR, pipeW := io.Pipe()
239
+	errChan := make(chan []byte)
240
+	// Collect stderr, we will use it in case of an error
241
+	go func() {
242
+		errText, e := ioutil.ReadAll(stderr)
243
+		if e != nil {
244
+			errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
245
+		}
246
+		errChan <- errText
247
+	}()
248
+	// Copy stdout to the returned pipe
249
+	go func() {
250
+		_, err := io.Copy(pipeW, stdout)
251
+		if err != nil {
252
+			pipeW.CloseWithError(err)
253
+		}
254
+		errText := <-errChan
255
+		if err := cmd.Wait(); err != nil {
256
+			pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errText))
257
+		} else {
258
+			pipeW.Close()
259
+		}
260
+	}()
261
+	// Run the command and return the pipe
262
+	if err := cmd.Start(); err != nil {
263
+		return nil, err
264
+	}
265
+	return pipeR, nil
266
+}
267
+
268
+// NewTempArchive reads the content of src into a temporary file, and returns the contents
269
+// of that file as an archive. The archive can only be read once - as soon as reading completes,
270
+// the file will be deleted.
271
+func NewTempArchive(src Archive, dir string) (*TempArchive, error) {
272
+	f, err := ioutil.TempFile(dir, "")
273
+	if err != nil {
274
+		return nil, err
275
+	}
276
+	if _, err := io.Copy(f, src); err != nil {
277
+		return nil, err
278
+	}
279
+	if _, err := f.Seek(0, 0); err != nil {
280
+		return nil, err
281
+	}
282
+	st, err := f.Stat()
283
+	if err != nil {
284
+		return nil, err
285
+	}
286
+	size := st.Size()
287
+	return &TempArchive{f, size}, nil
288
+}
289
+
290
+type TempArchive struct {
291
+	*os.File
292
+	Size int64 // Pre-computed from Stat().Size() as a convenience
293
+}
294
+
295
+func (archive *TempArchive) Read(data []byte) (int, error) {
296
+	n, err := archive.File.Read(data)
297
+	if err != nil {
298
+		os.Remove(archive.File.Name())
299
+	}
300
+	return n, err
301
+}
0 302
new file mode 100644
... ...
@@ -0,0 +1,118 @@
0
+package archive
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"io"
6
+	"io/ioutil"
7
+	"os"
8
+	"os/exec"
9
+	"path"
10
+	"testing"
11
+	"time"
12
+)
13
+
14
+func TestCmdStreamLargeStderr(t *testing.T) {
15
+	cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
16
+	out, err := CmdStream(cmd)
17
+	if err != nil {
18
+		t.Fatalf("Failed to start command: %s", err)
19
+	}
20
+	errCh := make(chan error)
21
+	go func() {
22
+		_, err := io.Copy(ioutil.Discard, out)
23
+		errCh <- err
24
+	}()
25
+	select {
26
+	case err := <-errCh:
27
+		if err != nil {
28
+			t.Fatalf("Command should not have failed (err=%.100s...)", err)
29
+		}
30
+	case <-time.After(5 * time.Second):
31
+		t.Fatalf("Command did not complete in 5 seconds; probable deadlock")
32
+	}
33
+}
34
+
35
+func TestCmdStreamBad(t *testing.T) {
36
+	badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
37
+	out, err := CmdStream(badCmd)
38
+	if err != nil {
39
+		t.Fatalf("Failed to start command: %s", err)
40
+	}
41
+	if output, err := ioutil.ReadAll(out); err == nil {
42
+		t.Fatalf("Command should have failed")
43
+	} else if err.Error() != "exit status 1: error couldn't reverse the phase pulser\n" {
44
+		t.Fatalf("Wrong error value (%s)", err)
45
+	} else if s := string(output); s != "hello\n" {
46
+		t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
47
+	}
48
+}
49
+
50
+func TestCmdStreamGood(t *testing.T) {
51
+	cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
52
+	out, err := CmdStream(cmd)
53
+	if err != nil {
54
+		t.Fatal(err)
55
+	}
56
+	if output, err := ioutil.ReadAll(out); err != nil {
57
+		t.Fatalf("Command should not have failed (err=%s)", err)
58
+	} else if s := string(output); s != "hello\n" {
59
+		t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
60
+	}
61
+}
62
+
63
+func tarUntar(t *testing.T, origin string, compression Compression) error {
64
+	archive, err := Tar(origin, compression)
65
+	if err != nil {
66
+		t.Fatal(err)
67
+	}
68
+
69
+	buf := make([]byte, 10)
70
+	if _, err := archive.Read(buf); err != nil {
71
+		return err
72
+	}
73
+	archive = io.MultiReader(bytes.NewReader(buf), archive)
74
+
75
+	detectedCompression := DetectCompression(buf)
76
+	if detectedCompression.Extension() != compression.Extension() {
77
+		return fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
78
+	}
79
+
80
+	tmp, err := ioutil.TempDir("", "docker-test-untar")
81
+	if err != nil {
82
+		return err
83
+	}
84
+	defer os.RemoveAll(tmp)
85
+	if err := Untar(archive, tmp); err != nil {
86
+		return err
87
+	}
88
+	if _, err := os.Stat(tmp); err != nil {
89
+		return err
90
+	}
91
+	return nil
92
+}
93
+
94
+func TestTarUntar(t *testing.T) {
95
+	origin, err := ioutil.TempDir("", "docker-test-untar-origin")
96
+	if err != nil {
97
+		t.Fatal(err)
98
+	}
99
+	defer os.RemoveAll(origin)
100
+	if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
101
+		t.Fatal(err)
102
+	}
103
+	if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
104
+		t.Fatal(err)
105
+	}
106
+
107
+	for _, c := range []Compression{
108
+		Uncompressed,
109
+		Gzip,
110
+		Bzip2,
111
+		Xz,
112
+	} {
113
+		if err := tarUntar(t, origin, c); err != nil {
114
+			t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
115
+		}
116
+	}
117
+}
0 118
deleted file mode 100644
... ...
@@ -1,118 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"bytes"
5
-	"fmt"
6
-	"io"
7
-	"io/ioutil"
8
-	"os"
9
-	"os/exec"
10
-	"path"
11
-	"testing"
12
-	"time"
13
-)
14
-
15
-func TestCmdStreamLargeStderr(t *testing.T) {
16
-	cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
17
-	out, err := CmdStream(cmd)
18
-	if err != nil {
19
-		t.Fatalf("Failed to start command: %s", err)
20
-	}
21
-	errCh := make(chan error)
22
-	go func() {
23
-		_, err := io.Copy(ioutil.Discard, out)
24
-		errCh <- err
25
-	}()
26
-	select {
27
-	case err := <-errCh:
28
-		if err != nil {
29
-			t.Fatalf("Command should not have failed (err=%.100s...)", err)
30
-		}
31
-	case <-time.After(5 * time.Second):
32
-		t.Fatalf("Command did not complete in 5 seconds; probable deadlock")
33
-	}
34
-}
35
-
36
-func TestCmdStreamBad(t *testing.T) {
37
-	badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
38
-	out, err := CmdStream(badCmd)
39
-	if err != nil {
40
-		t.Fatalf("Failed to start command: %s", err)
41
-	}
42
-	if output, err := ioutil.ReadAll(out); err == nil {
43
-		t.Fatalf("Command should have failed")
44
-	} else if err.Error() != "exit status 1: error couldn't reverse the phase pulser\n" {
45
-		t.Fatalf("Wrong error value (%s)", err)
46
-	} else if s := string(output); s != "hello\n" {
47
-		t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
48
-	}
49
-}
50
-
51
-func TestCmdStreamGood(t *testing.T) {
52
-	cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
53
-	out, err := CmdStream(cmd)
54
-	if err != nil {
55
-		t.Fatal(err)
56
-	}
57
-	if output, err := ioutil.ReadAll(out); err != nil {
58
-		t.Fatalf("Command should not have failed (err=%s)", err)
59
-	} else if s := string(output); s != "hello\n" {
60
-		t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output)
61
-	}
62
-}
63
-
64
-func tarUntar(t *testing.T, origin string, compression Compression) error {
65
-	archive, err := Tar(origin, compression)
66
-	if err != nil {
67
-		t.Fatal(err)
68
-	}
69
-
70
-	buf := make([]byte, 10)
71
-	if _, err := archive.Read(buf); err != nil {
72
-		return err
73
-	}
74
-	archive = io.MultiReader(bytes.NewReader(buf), archive)
75
-
76
-	detectedCompression := DetectCompression(buf)
77
-	if detectedCompression.Extension() != compression.Extension() {
78
-		return fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension())
79
-	}
80
-
81
-	tmp, err := ioutil.TempDir("", "docker-test-untar")
82
-	if err != nil {
83
-		return err
84
-	}
85
-	defer os.RemoveAll(tmp)
86
-	if err := Untar(archive, tmp); err != nil {
87
-		return err
88
-	}
89
-	if _, err := os.Stat(tmp); err != nil {
90
-		return err
91
-	}
92
-	return nil
93
-}
94
-
95
-func TestTarUntar(t *testing.T) {
96
-	origin, err := ioutil.TempDir("", "docker-test-untar-origin")
97
-	if err != nil {
98
-		t.Fatal(err)
99
-	}
100
-	defer os.RemoveAll(origin)
101
-	if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil {
102
-		t.Fatal(err)
103
-	}
104
-	if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil {
105
-		t.Fatal(err)
106
-	}
107
-
108
-	for _, c := range []Compression{
109
-		Uncompressed,
110
-		Gzip,
111
-		Bzip2,
112
-		Xz,
113
-	} {
114
-		if err := tarUntar(t, origin, c); err != nil {
115
-			t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err)
116
-		}
117
-	}
118
-}
... ...
@@ -3,6 +3,7 @@ package docker
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6
+	"github.com/dotcloud/docker/archive"
6 7
 	"github.com/dotcloud/docker/utils"
7 8
 	"io"
8 9
 	"io/ioutil"
... ...
@@ -64,6 +65,9 @@ func (b *buildFile) CmdFrom(name string) error {
64 64
 	}
65 65
 	b.image = image.ID
66 66
 	b.config = &Config{}
67
+	if image.Config != nil {
68
+		b.config = image.Config
69
+	}
67 70
 	if b.config.Env == nil || len(b.config.Env) == 0 {
68 71
 		b.config.Env = append(b.config.Env, "HOME=/", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
69 72
 	}
... ...
@@ -291,17 +295,17 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error {
291 291
 		return fmt.Errorf("%s: no such file or directory", orig)
292 292
 	}
293 293
 	if fi.IsDir() {
294
-		if err := CopyWithTar(origPath, destPath); err != nil {
294
+		if err := archive.CopyWithTar(origPath, destPath); err != nil {
295 295
 			return err
296 296
 		}
297 297
 		// First try to unpack the source as an archive
298
-	} else if err := UntarPath(origPath, destPath); err != nil {
298
+	} else if err := archive.UntarPath(origPath, destPath); err != nil {
299 299
 		utils.Debugf("Couldn't untar %s to %s: %s", origPath, destPath, err)
300 300
 		// If that fails, just copy it as a regular file
301 301
 		if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
302 302
 			return err
303 303
 		}
304
-		if err := CopyWithTar(origPath, destPath); err != nil {
304
+		if err := archive.CopyWithTar(origPath, destPath); err != nil {
305 305
 			return err
306 306
 		}
307 307
 	}
... ...
@@ -387,8 +391,7 @@ func (b *buildFile) run() (string, error) {
387 387
 	}
388 388
 
389 389
 	//start the container
390
-	hostConfig := &HostConfig{}
391
-	if err := c.Start(hostConfig); err != nil {
390
+	if err := c.Start(); err != nil {
392 391
 		return "", err
393 392
 	}
394 393
 
... ...
@@ -473,7 +476,7 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
473 473
 	if err != nil {
474 474
 		return "", err
475 475
 	}
476
-	if err := Untar(context, name); err != nil {
476
+	if err := archive.Untar(context, name); err != nil {
477 477
 		return "", err
478 478
 	}
479 479
 	defer os.RemoveAll(name)
... ...
@@ -2,6 +2,7 @@ package docker
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"github.com/dotcloud/docker/archive"
5 6
 	"io/ioutil"
6 7
 	"net"
7 8
 	"net/http"
... ...
@@ -12,7 +13,7 @@ import (
12 12
 
13 13
 // mkTestContext generates a build context from the contents of the provided dockerfile.
14 14
 // This context is suitable for use as an argument to BuildFile.Build()
15
-func mkTestContext(dockerfile string, files [][2]string, t *testing.T) Archive {
15
+func mkTestContext(dockerfile string, files [][2]string, t *testing.T) archive.Archive {
16 16
 	context, err := mkBuildContext(dockerfile, files)
17 17
 	if err != nil {
18 18
 		t.Fatal(err)
... ...
@@ -541,3 +542,39 @@ func TestBuildADDFileNotFound(t *testing.T) {
541 541
 		t.Fail()
542 542
 	}
543 543
 }
544
+
545
+func TestBuildInheritance(t *testing.T) {
546
+	runtime, err := newTestRuntime("")
547
+	if err != nil {
548
+		t.Fatal(err)
549
+	}
550
+	defer nuke(runtime)
551
+
552
+	srv := &Server{
553
+		runtime:     runtime,
554
+		pullingPool: make(map[string]struct{}),
555
+		pushingPool: make(map[string]struct{}),
556
+	}
557
+
558
+	img := buildImage(testContextTemplate{`
559
+            from {IMAGE}
560
+            expose 4243
561
+            `,
562
+		nil, nil}, t, srv, true)
563
+
564
+	img2 := buildImage(testContextTemplate{fmt.Sprintf(`
565
+            from %s
566
+            entrypoint ["/bin/echo"]
567
+            `, img.ID),
568
+		nil, nil}, t, srv, true)
569
+
570
+	// from child
571
+	if img2.Config.Entrypoint[0] != "/bin/echo" {
572
+		t.Fail()
573
+	}
574
+
575
+	// from parent
576
+	if img.Config.PortSpecs[0] != "4243" {
577
+		t.Fail()
578
+	}
579
+}
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"errors"
10 10
 	"flag"
11 11
 	"fmt"
12
+	"github.com/dotcloud/docker/archive"
12 13
 	"github.com/dotcloud/docker/auth"
13 14
 	"github.com/dotcloud/docker/registry"
14 15
 	"github.com/dotcloud/docker/term"
... ...
@@ -137,7 +138,7 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
137 137
 
138 138
 // mkBuildContext returns an archive of an empty context with the contents
139 139
 // of `dockerfile` at the path ./Dockerfile
140
-func mkBuildContext(dockerfile string, files [][2]string) (Archive, error) {
140
+func mkBuildContext(dockerfile string, files [][2]string) (archive.Archive, error) {
141 141
 	buf := new(bytes.Buffer)
142 142
 	tw := tar.NewWriter(buf)
143 143
 	files = append(files, [2]string{"Dockerfile", dockerfile})
... ...
@@ -175,7 +176,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
175 175
 	}
176 176
 
177 177
 	var (
178
-		context  Archive
178
+		context  archive.Archive
179 179
 		isRemote bool
180 180
 		err      error
181 181
 	)
... ...
@@ -194,7 +195,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
194 194
 		if _, err := os.Stat(cmd.Arg(0)); err != nil {
195 195
 			return err
196 196
 		}
197
-		context, err = Tar(cmd.Arg(0), Uncompressed)
197
+		context, err = archive.Tar(cmd.Arg(0), archive.Uncompressed)
198 198
 	}
199 199
 	var body io.Reader
200 200
 	// Setup an upload progress bar
... ...
@@ -491,7 +492,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
491 491
 }
492 492
 
493 493
 func (cli *DockerCli) CmdStop(args ...string) error {
494
-	cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container")
494
+	cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container (Send SIGTERM, and then SIGKILL after grace period)")
495 495
 	nSeconds := cmd.Int("t", 10, "Number of seconds to wait for the container to stop before killing it.")
496 496
 	if err := cmd.Parse(args); err != nil {
497 497
 		return nil
... ...
@@ -654,7 +655,11 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
654 654
 		if err != nil {
655 655
 			obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
656 656
 			if err != nil {
657
-				fmt.Fprintf(cli.err, "No such image or container: %s\n", name)
657
+				if strings.Contains(err.Error(), "No such") {
658
+					fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
659
+				} else {
660
+					fmt.Fprintf(cli.err, "%s", err)
661
+				}
658 662
 				status = 1
659 663
 				continue
660 664
 			}
... ...
@@ -667,9 +672,11 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
667 667
 		}
668 668
 		indented.WriteString(",")
669 669
 	}
670
-	// Remove trailling ','
671
-	indented.Truncate(indented.Len() - 1)
672 670
 
671
+	if indented.Len() > 0 {
672
+		// Remove trailing ','
673
+		indented.Truncate(indented.Len() - 1)
674
+	}
673 675
 	fmt.Fprintf(cli.out, "[")
674 676
 	if _, err := io.Copy(cli.out, indented); err != nil {
675 677
 		return err
... ...
@@ -682,7 +689,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
682 682
 }
683 683
 
684 684
 func (cli *DockerCli) CmdTop(args ...string) error {
685
-	cmd := Subcmd("top", "CONTAINER", "Lookup the running processes of a container")
685
+	cmd := Subcmd("top", "CONTAINER [ps OPTIONS]", "Lookup the running processes of a container")
686 686
 	if err := cmd.Parse(args); err != nil {
687 687
 		return nil
688 688
 	}
... ...
@@ -788,7 +795,10 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
788 788
 }
789 789
 
790 790
 func (cli *DockerCli) CmdHistory(args ...string) error {
791
-	cmd := Subcmd("history", "IMAGE", "Show the history of an image")
791
+	cmd := Subcmd("history", "[OPTIONS] IMAGE", "Show the history of an image")
792
+	quiet := cmd.Bool("q", false, "only show numeric IDs")
793
+	noTrunc := cmd.Bool("notrunc", false, "Don't truncate output")
794
+
792 795
 	if err := cmd.Parse(args); err != nil {
793 796
 		return nil
794 797
 	}
... ...
@@ -807,14 +817,35 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
807 807
 	if err != nil {
808 808
 		return err
809 809
 	}
810
+
810 811
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
811
-	fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
812
+	if !*quiet {
813
+		fmt.Fprintln(w, "IMAGE\tCREATED\tCREATED BY\tSIZE")
814
+	}
812 815
 
813 816
 	for _, out := range outs {
814
-		if out.Tags != nil {
815
-			out.ID = out.Tags[0]
817
+		if !*quiet {
818
+			if *noTrunc {
819
+				fmt.Fprintf(w, "%s\t", out.ID)
820
+			} else {
821
+				fmt.Fprintf(w, "%s\t", utils.TruncateID(out.ID))
822
+			}
823
+
824
+			fmt.Fprintf(w, "%s ago\t", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))))
825
+
826
+			if *noTrunc {
827
+				fmt.Fprintf(w, "%s\t", out.CreatedBy)
828
+			} else {
829
+				fmt.Fprintf(w, "%s\t", utils.Trunc(out.CreatedBy, 45))
830
+			}
831
+			fmt.Fprintf(w, "%s\n", utils.HumanSize(out.Size))
832
+		} else {
833
+			if *noTrunc {
834
+				fmt.Fprintln(w, out.ID)
835
+			} else {
836
+				fmt.Fprintln(w, utils.TruncateID(out.ID))
837
+			}
816 838
 		}
817
-		fmt.Fprintf(w, "%s \t%s ago\t%s\n", out.ID, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy)
818 839
 	}
819 840
 	w.Flush()
820 841
 	return nil
... ...
@@ -852,7 +883,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
852 852
 
853 853
 // 'docker kill NAME' kills a running container
854 854
 func (cli *DockerCli) CmdKill(args ...string) error {
855
-	cmd := Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container")
855
+	cmd := Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container (send SIGKILL)")
856 856
 	if err := cmd.Parse(args); err != nil {
857 857
 		return nil
858 858
 	}
... ...
@@ -873,7 +904,7 @@ func (cli *DockerCli) CmdKill(args ...string) error {
873 873
 }
874 874
 
875 875
 func (cli *DockerCli) CmdImport(args ...string) error {
876
-	cmd := Subcmd("import", "URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball(.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).")
876
+	cmd := Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create a new filesystem image from the contents of a tarball(.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).")
877 877
 
878 878
 	if err := cmd.Parse(args); err != nil {
879 879
 		return nil
... ...
@@ -882,7 +913,8 @@ func (cli *DockerCli) CmdImport(args ...string) error {
882 882
 		cmd.Usage()
883 883
 		return nil
884 884
 	}
885
-	src, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
885
+	src := cmd.Arg(0)
886
+	repository, tag := utils.ParseRepositoryTag(cmd.Arg(1))
886 887
 	v := url.Values{}
887 888
 	v.Set("repo", repository)
888 889
 	v.Set("tag", tag)
... ...
@@ -1062,7 +1094,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
1062 1062
 
1063 1063
 		w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1064 1064
 		if !*quiet {
1065
-			fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tSIZE")
1065
+			fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE")
1066 1066
 		}
1067 1067
 
1068 1068
 		for _, out := range outs {
... ...
@@ -1155,7 +1187,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1155 1155
 	}
1156 1156
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1157 1157
 	if !*quiet {
1158
-		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
1158
+		fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
1159 1159
 		if *size {
1160 1160
 			fmt.Fprintln(w, "\tSIZE")
1161 1161
 		} else {
... ...
@@ -1199,14 +1231,16 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1199 1199
 }
1200 1200
 
1201 1201
 func (cli *DockerCli) CmdCommit(args ...string) error {
1202
-	cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
1202
+	cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes")
1203 1203
 	flComment := cmd.String("m", "", "Commit message")
1204 1204
 	flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
1205 1205
 	flConfig := cmd.String("run", "", "Config automatically applied when the image is run. "+`(ex: {"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`)
1206 1206
 	if err := cmd.Parse(args); err != nil {
1207 1207
 		return nil
1208 1208
 	}
1209
-	name, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
1209
+	name := cmd.Arg(0)
1210
+	repository, tag := utils.ParseRepositoryTag(cmd.Arg(1))
1211
+
1210 1212
 	if name == "" {
1211 1213
 		cmd.Usage()
1212 1214
 		return nil
... ...
@@ -1316,8 +1350,18 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
1316 1316
 		return nil
1317 1317
 	}
1318 1318
 	name := cmd.Arg(0)
1319
+	body, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
1320
+	if err != nil {
1321
+		return err
1322
+	}
1319 1323
 
1320
-	if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err, nil); err != nil {
1324
+	container := &Container{}
1325
+	err = json.Unmarshal(body, container)
1326
+	if err != nil {
1327
+		return err
1328
+	}
1329
+
1330
+	if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", container.Config.Tty, nil, cli.out, cli.err, nil); err != nil {
1321 1331
 		return err
1322 1332
 	}
1323 1333
 	return nil
... ...
@@ -1379,8 +1423,10 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
1379 1379
 }
1380 1380
 
1381 1381
 func (cli *DockerCli) CmdSearch(args ...string) error {
1382
-	cmd := Subcmd("search", "NAME", "Search the docker index for images")
1382
+	cmd := Subcmd("search", "TERM", "Search the docker index for images")
1383 1383
 	noTrunc := cmd.Bool("notrunc", false, "Don't truncate output")
1384
+	trusted := cmd.Bool("trusted", false, "Only show trusted builds")
1385
+	stars := cmd.Int("stars", 0, "Only displays with at least xxx stars")
1384 1386
 	if err := cmd.Parse(args); err != nil {
1385 1387
 		return nil
1386 1388
 	}
... ...
@@ -1396,27 +1442,32 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
1396 1396
 		return err
1397 1397
 	}
1398 1398
 
1399
-	outs := []APISearch{}
1399
+	outs := []registry.SearchResult{}
1400 1400
 	err = json.Unmarshal(body, &outs)
1401 1401
 	if err != nil {
1402 1402
 		return err
1403 1403
 	}
1404
-	fmt.Fprintf(cli.out, "Found %d results matching your query (\"%s\")\n", len(outs), cmd.Arg(0))
1405
-	w := tabwriter.NewWriter(cli.out, 33, 1, 3, ' ', 0)
1406
-	fmt.Fprintf(w, "NAME\tDESCRIPTION\n")
1407
-	_, width := cli.getTtySize()
1408
-	if width == 0 {
1409
-		width = 45
1410
-	} else {
1411
-		width = width - 33 //remote the first column
1412
-	}
1404
+	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
1405
+	fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tTRUSTED\n")
1413 1406
 	for _, out := range outs {
1407
+		if (*trusted && !out.IsTrusted) || (*stars > out.StarCount) {
1408
+			continue
1409
+		}
1414 1410
 		desc := strings.Replace(out.Description, "\n", " ", -1)
1415 1411
 		desc = strings.Replace(desc, "\r", " ", -1)
1416
-		if !*noTrunc && len(desc) > width {
1417
-			desc = utils.Trunc(desc, width-3) + "..."
1412
+		if !*noTrunc && len(desc) > 45 {
1413
+			desc = utils.Trunc(desc, 42) + "..."
1414
+		}
1415
+		fmt.Fprintf(w, "%s\t%s\t%d\t", out.Name, desc, out.StarCount)
1416
+		if out.IsOfficial {
1417
+			fmt.Fprint(w, "[OK]")
1418
+
1418 1419
 		}
1419
-		fmt.Fprintf(w, "%s\t%s\n", out.Name, desc)
1420
+		fmt.Fprint(w, "\t")
1421
+		if out.IsTrusted {
1422
+			fmt.Fprint(w, "[OK]")
1423
+		}
1424
+		fmt.Fprint(w, "\n")
1420 1425
 	}
1421 1426
 	w.Flush()
1422 1427
 	return nil
... ...
@@ -1484,7 +1535,7 @@ func (opts PathOpts) Set(val string) error {
1484 1484
 }
1485 1485
 
1486 1486
 func (cli *DockerCli) CmdTag(args ...string) error {
1487
-	cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
1487
+	cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY[:TAG]", "Tag an image into a repository")
1488 1488
 	force := cmd.Bool("f", false, "Force")
1489 1489
 	if err := cmd.Parse(args); err != nil {
1490 1490
 		return nil
... ...
@@ -1495,10 +1546,10 @@ func (cli *DockerCli) CmdTag(args ...string) error {
1495 1495
 	}
1496 1496
 
1497 1497
 	v := url.Values{}
1498
-	v.Set("repo", cmd.Arg(1))
1499
-	if cmd.NArg() == 3 {
1500
-		v.Set("tag", cmd.Arg(2))
1501
-	}
1498
+	repository, tag := utils.ParseRepositoryTag(cmd.Arg(1))
1499
+
1500
+	v.Set("repo", repository)
1501
+	v.Set("tag", tag)
1502 1502
 
1503 1503
 	if *force {
1504 1504
 		v.Set("force", "1")
... ...
@@ -1749,7 +1800,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
1749 1749
 
1750 1750
 	if statusCode == 200 {
1751 1751
 		r := bytes.NewReader(data)
1752
-		if err := Untar(r, copyData.HostPath); err != nil {
1752
+		if err := archive.Untar(r, copyData.HostPath); err != nil {
1753 1753
 			return err
1754 1754
 		}
1755 1755
 	}
... ...
@@ -645,3 +645,57 @@ func TestRunAutoRemove(t *testing.T) {
645 645
 		t.Fatalf("failed to remove container automatically: container %s still exists", temporaryContainerID)
646 646
 	}
647 647
 }
648
+
649
+func TestCmdLogs(t *testing.T) {
650
+	cli := NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr)
651
+	defer cleanup(globalRuntime)
652
+
653
+	if err := cli.CmdRun(unitTestImageID, "sh", "-c", "ls -l"); err != nil {
654
+		t.Fatal(err)
655
+	}
656
+	if err := cli.CmdRun("-t", unitTestImageID, "sh", "-c", "ls -l"); err != nil {
657
+		t.Fatal(err)
658
+	}
659
+
660
+	if err := cli.CmdLogs(globalRuntime.List()[0].ID); err != nil {
661
+		t.Fatal(err)
662
+	}
663
+}
664
+
665
+// Expected behaviour: using / as a bind mount source should throw an error
666
+func TestRunErrorBindMountRootSource(t *testing.T) {
667
+
668
+	cli := NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr)
669
+	defer cleanup(globalRuntime)
670
+
671
+	c := make(chan struct{})
672
+	go func() {
673
+		defer close(c)
674
+		if err := cli.CmdRun("-v", "/:/tmp", unitTestImageID, "echo 'should fail'"); err == nil {
675
+			t.Fatal("should have failed to run when using / as a source for the bind mount")
676
+		}
677
+	}()
678
+
679
+	setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
680
+		<-c
681
+	})
682
+}
683
+
684
+// Expected behaviour: error out when attempting to bind mount non-existing source paths
685
+func TestRunErrorBindNonExistingSource(t *testing.T) {
686
+
687
+	cli := NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr)
688
+	defer cleanup(globalRuntime)
689
+
690
+	c := make(chan struct{})
691
+	go func() {
692
+		defer close(c)
693
+		if err := cli.CmdRun("-v", "/i/dont/exist:/tmp", unitTestImageID, "echo 'should fail'"); err == nil {
694
+			t.Fatal("should have failed to run when using /i/dont/exist as a source for the bind mount")
695
+		}
696
+	}()
697
+
698
+	setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
699
+		<-c
700
+	})
701
+}
... ...
@@ -2,11 +2,13 @@ package docker
2 2
 
3 3
 import (
4 4
 	"net"
5
+	"github.com/dotcloud/docker/engine"
5 6
 )
6 7
 
8
+// FIXME: separate runtime configuration from http api configuration
7 9
 type DaemonConfig struct {
8 10
 	Pidfile                     string
9
-	GraphPath                   string
11
+	Root                        string
10 12
 	ProtoAddresses              []string
11 13
 	AutoRestart                 bool
12 14
 	EnableCors                  bool
... ...
@@ -16,3 +18,26 @@ type DaemonConfig struct {
16 16
 	DefaultIp                   net.IP
17 17
 	InterContainerCommunication bool
18 18
 }
19
+
20
+// ConfigFromJob creates and returns a new DaemonConfig object
21
+// by parsing the contents of a job's environment.
22
+func ConfigFromJob(job *engine.Job) *DaemonConfig {
23
+	var config DaemonConfig
24
+	config.Pidfile = job.Getenv("Pidfile")
25
+	config.Root = job.Getenv("Root")
26
+	config.AutoRestart = job.GetenvBool("AutoRestart")
27
+	config.EnableCors = job.GetenvBool("EnableCors")
28
+	if dns := job.Getenv("Dns"); dns != "" {
29
+		config.Dns = []string{dns}
30
+	}
31
+	config.EnableIptables = job.GetenvBool("EnableIptables")
32
+	if br := job.Getenv("BridgeIface"); br != "" {
33
+		config.BridgeIface = br
34
+	} else {
35
+		config.BridgeIface = DefaultNetworkBridge
36
+	}
37
+	config.ProtoAddresses = job.GetenvList("ProtoAddresses")
38
+	config.DefaultIp = net.ParseIP(job.Getenv("DefaultIp"))
39
+	config.InterContainerCommunication = job.GetenvBool("InterContainerCommunication")
40
+	return &config
41
+}
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"errors"
7 7
 	"flag"
8 8
 	"fmt"
9
+	"github.com/dotcloud/docker/archive"
9 10
 	"github.com/dotcloud/docker/term"
10 11
 	"github.com/dotcloud/docker/utils"
11 12
 	"github.com/kr/pty"
... ...
@@ -59,11 +60,15 @@ type Container struct {
59 59
 	Volumes  map[string]string
60 60
 	// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
61 61
 	// Easier than migrating older container configs :)
62
-	VolumesRW map[string]bool
62
+	VolumesRW  map[string]bool
63
+	hostConfig *HostConfig
63 64
 
64 65
 	activeLinks map[string]*Link
65 66
 }
66 67
 
68
+// Note: the Config structure should hold only portable information about the container.
69
+// Here, "portable" means "independent from the host we are running on".
70
+// Non-portable information *should* appear in HostConfig.
67 71
 type Config struct {
68 72
 	Hostname        string
69 73
 	Domainname      string
... ...
@@ -88,15 +93,16 @@ type Config struct {
88 88
 	WorkingDir      string
89 89
 	Entrypoint      []string
90 90
 	NetworkDisabled bool
91
-	Privileged      bool
92 91
 }
93 92
 
94 93
 type HostConfig struct {
95 94
 	Binds           []string
96 95
 	ContainerIDFile string
97 96
 	LxcConf         []KeyValuePair
97
+	Privileged      bool
98 98
 	PortBindings    map[Port][]PortBinding
99 99
 	Links           []string
100
+	PublishAllPorts bool
100 101
 }
101 102
 
102 103
 type BindMap struct {
... ...
@@ -168,6 +174,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
168 168
 	flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)")
169 169
 	cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)")
170 170
 	cmd.String("name", "", "Assign a name to the container")
171
+	flPublishAll := cmd.Bool("P", false, "Publish all exposed ports to the host interfaces")
171 172
 
172 173
 	if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit {
173 174
 		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
... ...
@@ -226,12 +233,27 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
226 226
 		}
227 227
 	}
228 228
 
229
+	envs := []string{}
230
+
231
+	for _, env := range flEnv {
232
+		arr := strings.Split(env, "=")
233
+		if len(arr) > 1 {
234
+			envs = append(envs, env)
235
+		} else {
236
+			v := os.Getenv(env)
237
+			envs = append(envs, env+"="+v)
238
+		}
239
+	}
240
+
229 241
 	var binds []string
230 242
 
231 243
 	// add any bind targets to the list of container volumes
232 244
 	for bind := range flVolumes {
233 245
 		arr := strings.Split(bind, ":")
234 246
 		if len(arr) > 1 {
247
+			if arr[0] == "/" {
248
+				return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
249
+			}
235 250
 			dstDir := arr[1]
236 251
 			flVolumes[dstDir] = struct{}{}
237 252
 			binds = append(binds, bind)
... ...
@@ -285,7 +307,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
285 285
 	}
286 286
 
287 287
 	config := &Config{
288
-		Hostname:        *flHostname,
288
+		Hostname:        hostname,
289 289
 		Domainname:      domainname,
290 290
 		PortSpecs:       nil, // Deprecated
291 291
 		ExposedPorts:    ports,
... ...
@@ -298,14 +320,13 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
298 298
 		AttachStdin:     flAttach.Get("stdin"),
299 299
 		AttachStdout:    flAttach.Get("stdout"),
300 300
 		AttachStderr:    flAttach.Get("stderr"),
301
-		Env:             flEnv,
301
+		Env:             envs,
302 302
 		Cmd:             runCmd,
303 303
 		Dns:             flDns,
304 304
 		Image:           image,
305 305
 		Volumes:         flVolumes,
306 306
 		VolumesFrom:     strings.Join(flVolumesFrom, ","),
307 307
 		Entrypoint:      entrypoint,
308
-		Privileged:      *flPrivileged,
309 308
 		WorkingDir:      *flWorkingDir,
310 309
 	}
311 310
 
... ...
@@ -313,8 +334,10 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
313 313
 		Binds:           binds,
314 314
 		ContainerIDFile: *flContainerIDFile,
315 315
 		LxcConf:         lxcConf,
316
+		Privileged:      *flPrivileged,
316 317
 		PortBindings:    portBindings,
317 318
 		Links:           flLinks,
319
+		PublishAllPorts: *flPublishAll,
318 320
 	}
319 321
 
320 322
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
... ...
@@ -367,11 +390,22 @@ func (settings *NetworkSettings) PortMappingAPI() []APIPort {
367 367
 
368 368
 // Inject the io.Reader at the given path. Note: do not close the reader
369 369
 func (container *Container) Inject(file io.Reader, pth string) error {
370
+	// Return error if path exists
371
+	if _, err := os.Stat(path.Join(container.rwPath(), pth)); err == nil {
372
+		// Since err is nil, the path could be stat'd and it exists
373
+		return fmt.Errorf("%s exists", pth)
374
+	} else if ! os.IsNotExist(err) {
375
+		// Expect err might be that the file doesn't exist, so
376
+		// if it's some other error, return that. 
377
+
378
+		return err
379
+	}
380
+
370 381
 	// Make sure the directory exists
371 382
 	if err := os.MkdirAll(path.Join(container.rwPath(), path.Dir(pth)), 0755); err != nil {
372 383
 		return err
373 384
 	}
374
-	// FIXME: Handle permissions/already existing dest
385
+
375 386
 	dest, err := os.Create(path.Join(container.rwPath(), pth))
376 387
 	if err != nil {
377 388
 		return err
... ...
@@ -400,7 +434,7 @@ func (container *Container) FromDisk() error {
400 400
 	if err := json.Unmarshal(data, container); err != nil && !strings.Contains(err.Error(), "docker.PortMapping") {
401 401
 		return err
402 402
 	}
403
-	return nil
403
+	return container.readHostConfig()
404 404
 }
405 405
 
406 406
 func (container *Container) ToDisk() (err error) {
... ...
@@ -408,44 +442,53 @@ func (container *Container) ToDisk() (err error) {
408 408
 	if err != nil {
409 409
 		return
410 410
 	}
411
-	return ioutil.WriteFile(container.jsonPath(), data, 0666)
411
+	err = ioutil.WriteFile(container.jsonPath(), data, 0666)
412
+	if err != nil {
413
+		return
414
+	}
415
+	return container.writeHostConfig()
412 416
 }
413 417
 
414
-func (container *Container) ReadHostConfig() (*HostConfig, error) {
418
+func (container *Container) readHostConfig() error {
419
+	container.hostConfig = &HostConfig{}
420
+	// If the hostconfig file does not exist, do not read it.
421
+	// (We still have to initialize container.hostConfig,
422
+	// but that's OK, since we just did that above.)
423
+	_, err := os.Stat(container.hostConfigPath())
424
+	if os.IsNotExist(err) {
425
+		return nil
426
+	}
415 427
 	data, err := ioutil.ReadFile(container.hostConfigPath())
416 428
 	if err != nil {
417
-		return &HostConfig{}, err
418
-	}
419
-	hostConfig := &HostConfig{}
420
-	if err := json.Unmarshal(data, hostConfig); err != nil {
421
-		return &HostConfig{}, err
429
+		return err
422 430
 	}
423
-	return hostConfig, nil
431
+	return json.Unmarshal(data, container.hostConfig)
424 432
 }
425 433
 
426
-func (container *Container) SaveHostConfig(hostConfig *HostConfig) (err error) {
427
-	data, err := json.Marshal(hostConfig)
434
+func (container *Container) writeHostConfig() (err error) {
435
+	data, err := json.Marshal(container.hostConfig)
428 436
 	if err != nil {
429 437
 		return
430 438
 	}
431 439
 	return ioutil.WriteFile(container.hostConfigPath(), data, 0666)
432 440
 }
433 441
 
434
-func (container *Container) generateLXCConfig(hostConfig *HostConfig) error {
435
-	fo, err := os.Create(container.lxcConfigPath())
442
+func (container *Container) generateEnvConfig(env []string) error {
443
+	data, err := json.Marshal(env)
436 444
 	if err != nil {
437 445
 		return err
438 446
 	}
439
-	defer fo.Close()
440
-	if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
447
+	ioutil.WriteFile(container.EnvConfigPath(), data, 0600)
448
+	return nil
449
+}
450
+
451
+func (container *Container) generateLXCConfig() error {
452
+	fo, err := os.Create(container.lxcConfigPath())
453
+	if err != nil {
441 454
 		return err
442 455
 	}
443
-	if hostConfig != nil {
444
-		if err := LxcHostConfigTemplateCompiled.Execute(fo, hostConfig); err != nil {
445
-			return err
446
-		}
447
-	}
448
-	return nil
456
+	defer fo.Close()
457
+	return LxcTemplateCompiled.Execute(fo, container)
449 458
 }
450 459
 
451 460
 func (container *Container) startPty() error {
... ...
@@ -640,7 +683,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
640 640
 	})
641 641
 }
642 642
 
643
-func (container *Container) Start(hostConfig *HostConfig) (err error) {
643
+func (container *Container) Start() (err error) {
644 644
 	container.State.Lock()
645 645
 	defer container.State.Unlock()
646 646
 	defer func() {
... ...
@@ -649,10 +692,6 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
649 649
 		}
650 650
 	}()
651 651
 
652
-	if hostConfig == nil { // in docker start of docker restart we want to reuse previous HostConfigFile
653
-		hostConfig, _ = container.ReadHostConfig()
654
-	}
655
-
656 652
 	if container.State.Running {
657 653
 		return fmt.Errorf("The container %s is already running.", container.ID)
658 654
 	}
... ...
@@ -662,7 +701,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
662 662
 	if container.runtime.networkManager.disabled {
663 663
 		container.Config.NetworkDisabled = true
664 664
 	} else {
665
-		if err := container.allocateNetwork(hostConfig); err != nil {
665
+		if err := container.allocateNetwork(); err != nil {
666 666
 			return err
667 667
 		}
668 668
 	}
... ...
@@ -686,7 +725,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
686 686
 	// Define illegal container destinations
687 687
 	illegalDsts := []string{"/", "."}
688 688
 
689
-	for _, bind := range hostConfig.Binds {
689
+	for _, bind := range container.hostConfig.Binds {
690 690
 		// FIXME: factorize bind parsing in parseBind
691 691
 		var src, dst, mode string
692 692
 		arr := strings.Split(bind, ":")
... ...
@@ -796,7 +835,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
796 796
 				}
797 797
 				if len(srcList) == 0 {
798 798
 					// If the source volume is empty copy files from the root into the volume
799
-					if err := CopyWithTar(rootVolPath, srcPath); err != nil {
799
+					if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
800 800
 						return err
801 801
 					}
802 802
 
... ...
@@ -820,7 +859,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
820 820
 		}
821 821
 	}
822 822
 
823
-	if err := container.generateLXCConfig(hostConfig); err != nil {
823
+	if err := container.generateLXCConfig(); err != nil {
824 824
 		return err
825 825
 	}
826 826
 
... ...
@@ -841,17 +880,17 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
841 841
 		params = append(params, "-u", container.Config.User)
842 842
 	}
843 843
 
844
-	if container.Config.Tty {
845
-		params = append(params, "-e", "TERM=xterm")
844
+	// Setup environment
845
+	env := []string{
846
+		"HOME=/",
847
+		"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
848
+		"container=lxc",
849
+		"HOSTNAME=" + container.Config.Hostname,
846 850
 	}
847 851
 
848
-	// Setup environment
849
-	params = append(params,
850
-		"-e", "HOME=/",
851
-		"-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
852
-		"-e", "container=lxc",
853
-		"-e", "HOSTNAME="+container.Config.Hostname,
854
-	)
852
+	if container.Config.Tty {
853
+		env = append(env, "TERM=xterm")
854
+	}
855 855
 
856 856
 	// Init any links between the parent and children
857 857
 	runtime := container.runtime
... ...
@@ -887,11 +926,19 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
887 887
 			}
888 888
 
889 889
 			for _, envVar := range link.ToEnv() {
890
-				params = append(params, "-e", envVar)
890
+				env = append(env, envVar)
891 891
 			}
892 892
 		}
893 893
 	}
894 894
 
895
+	for _, elem := range container.Config.Env {
896
+		env = append(env, elem)
897
+	}
898
+
899
+	if err := container.generateEnvConfig(env); err != nil {
900
+		return err
901
+	}
902
+
895 903
 	if container.Config.WorkingDir != "" {
896 904
 		workingDir := path.Clean(container.Config.WorkingDir)
897 905
 		utils.Debugf("[working dir] working dir is %s", workingDir)
... ...
@@ -905,16 +952,15 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
905 905
 		)
906 906
 	}
907 907
 
908
-	for _, elem := range container.Config.Env {
909
-		params = append(params, "-e", elem)
910
-	}
911
-
912 908
 	// Program
913 909
 	params = append(params, "--", container.Path)
914 910
 	params = append(params, container.Args...)
915 911
 
916
-	container.cmd = exec.Command("lxc-start", params...)
917
-
912
+	var lxcStart string = "lxc-start"
913
+	if container.hostConfig.Privileged && container.runtime.capabilities.AppArmor {
914
+		lxcStart = path.Join(container.runtime.config.Root, "lxc-start-unconfined")
915
+	}
916
+	container.cmd = exec.Command(lxcStart, params...)
918 917
 	// Setup logging of stdout and stderr to disk
919 918
 	if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
920 919
 		return err
... ...
@@ -941,8 +987,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
941 941
 	container.waitLock = make(chan struct{})
942 942
 
943 943
 	container.ToDisk()
944
-	container.SaveHostConfig(hostConfig)
945
-	go container.monitor(hostConfig)
944
+	go container.monitor()
946 945
 
947 946
 	defer utils.Debugf("Container running: %v", container.State.Running)
948 947
 	// We wait for the container to be fully running.
... ...
@@ -979,7 +1024,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
979 979
 }
980 980
 
981 981
 func (container *Container) Run() error {
982
-	if err := container.Start(&HostConfig{}); err != nil {
982
+	if err := container.Start(); err != nil {
983 983
 		return err
984 984
 	}
985 985
 	container.Wait()
... ...
@@ -992,8 +1037,7 @@ func (container *Container) Output() (output []byte, err error) {
992 992
 		return nil, err
993 993
 	}
994 994
 	defer pipe.Close()
995
-	hostConfig := &HostConfig{}
996
-	if err := container.Start(hostConfig); err != nil {
995
+	if err := container.Start(); err != nil {
997 996
 		return nil, err
998 997
 	}
999 998
 	output, err = ioutil.ReadAll(pipe)
... ...
@@ -1025,19 +1069,14 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
1025 1025
 	return utils.NewBufReader(reader), nil
1026 1026
 }
1027 1027
 
1028
-func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
1028
+func (container *Container) allocateNetwork() error {
1029 1029
 	if container.Config.NetworkDisabled {
1030 1030
 		return nil
1031 1031
 	}
1032 1032
 
1033 1033
 	var iface *NetworkInterface
1034 1034
 	var err error
1035
-	if !container.State.Ghost {
1036
-		iface, err = container.runtime.networkManager.Allocate()
1037
-		if err != nil {
1038
-			return err
1039
-		}
1040
-	} else {
1035
+	if container.State.Ghost {
1041 1036
 		manager := container.runtime.networkManager
1042 1037
 		if manager.disabled {
1043 1038
 			iface = &NetworkInterface{disabled: true}
... ...
@@ -1047,18 +1086,30 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
1047 1047
 				Gateway: manager.bridgeNetwork.IP,
1048 1048
 				manager: manager,
1049 1049
 			}
1050
-			ipNum := ipToInt(iface.IPNet.IP)
1051
-			manager.ipAllocator.inUse[ipNum] = struct{}{}
1050
+			if iface !=nil && iface.IPNet.IP != nil {
1051
+				ipNum := ipToInt(iface.IPNet.IP)
1052
+				manager.ipAllocator.inUse[ipNum] = struct{}{}
1053
+			} else {
1054
+				iface, err = container.runtime.networkManager.Allocate()
1055
+				if err != nil {
1056
+					return err
1057
+				}
1058
+			}
1059
+		}
1060
+	} else {
1061
+		iface, err = container.runtime.networkManager.Allocate()
1062
+		if err != nil {
1063
+			return err
1052 1064
 		}
1053 1065
 	}
1054 1066
 
1055 1067
 	if container.Config.PortSpecs != nil {
1056 1068
 		utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", "))
1057
-		if err := migratePortMappings(container.Config, hostConfig); err != nil {
1069
+		if err := migratePortMappings(container.Config, container.hostConfig); err != nil {
1058 1070
 			return err
1059 1071
 		}
1060 1072
 		container.Config.PortSpecs = nil
1061
-		if err := container.SaveHostConfig(hostConfig); err != nil {
1073
+		if err := container.writeHostConfig(); err != nil {
1062 1074
 			return err
1063 1075
 		}
1064 1076
 	}
... ...
@@ -1070,8 +1121,8 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
1070 1070
 		if container.Config.ExposedPorts != nil {
1071 1071
 			portSpecs = container.Config.ExposedPorts
1072 1072
 		}
1073
-		if hostConfig.PortBindings != nil {
1074
-			bindings = hostConfig.PortBindings
1073
+		if container.hostConfig.PortBindings != nil {
1074
+			bindings = container.hostConfig.PortBindings
1075 1075
 		}
1076 1076
 	} else {
1077 1077
 		if container.NetworkSettings.Ports != nil {
... ...
@@ -1086,6 +1137,9 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
1086 1086
 
1087 1087
 	for port := range portSpecs {
1088 1088
 		binding := bindings[port]
1089
+		if container.hostConfig.PublishAllPorts && len(binding) == 0 {
1090
+			binding = append(binding, PortBinding{})
1091
+		}
1089 1092
 		for i := 0; i < len(binding); i++ {
1090 1093
 			b := binding[i]
1091 1094
 			nat, err := iface.AllocatePort(port, b)
... ...
@@ -1098,7 +1152,7 @@ func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
1098 1098
 		}
1099 1099
 		bindings[port] = binding
1100 1100
 	}
1101
-	container.SaveHostConfig(hostConfig)
1101
+	container.writeHostConfig()
1102 1102
 
1103 1103
 	container.NetworkSettings.Ports = bindings
1104 1104
 	container.network = iface
... ...
@@ -1134,7 +1188,7 @@ func (container *Container) waitLxc() error {
1134 1134
 	}
1135 1135
 }
1136 1136
 
1137
-func (container *Container) monitor(hostConfig *HostConfig) {
1137
+func (container *Container) monitor() {
1138 1138
 	// Wait for the program to exit
1139 1139
 
1140 1140
 	// If the command does not exist, try to wait via lxc
... ...
@@ -1291,11 +1345,7 @@ func (container *Container) Restart(seconds int) error {
1291 1291
 	if err := container.Stop(seconds); err != nil {
1292 1292
 		return err
1293 1293
 	}
1294
-	hostConfig := &HostConfig{}
1295
-	if err := container.Start(hostConfig); err != nil {
1296
-		return err
1297
-	}
1298
-	return nil
1294
+	return container.Start()
1299 1295
 }
1300 1296
 
1301 1297
 // Wait blocks until the container stops running, then returns its exit code.
... ...
@@ -1312,23 +1362,23 @@ func (container *Container) Resize(h, w int) error {
1312 1312
 	return term.SetWinsize(pty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
1313 1313
 }
1314 1314
 
1315
-func (container *Container) ExportRw() (Archive, error) {
1316
-	return Tar(container.rwPath(), Uncompressed)
1315
+func (container *Container) ExportRw() (archive.Archive, error) {
1316
+	return archive.Tar(container.rwPath(), archive.Uncompressed)
1317 1317
 }
1318 1318
 
1319 1319
 func (container *Container) RwChecksum() (string, error) {
1320
-	rwData, err := Tar(container.rwPath(), Xz)
1320
+	rwData, err := archive.Tar(container.rwPath(), archive.Xz)
1321 1321
 	if err != nil {
1322 1322
 		return "", err
1323 1323
 	}
1324 1324
 	return utils.HashData(rwData)
1325 1325
 }
1326 1326
 
1327
-func (container *Container) Export() (Archive, error) {
1327
+func (container *Container) Export() (archive.Archive, error) {
1328 1328
 	if err := container.EnsureMounted(); err != nil {
1329 1329
 		return nil, err
1330 1330
 	}
1331
-	return Tar(container.RootfsPath(), Uncompressed)
1331
+	return archive.Tar(container.RootfsPath(), archive.Uncompressed)
1332 1332
 }
1333 1333
 
1334 1334
 func (container *Container) WaitTimeout(timeout time.Duration) error {
... ...
@@ -1416,6 +1466,10 @@ func (container *Container) jsonPath() string {
1416 1416
 	return path.Join(container.root, "config.json")
1417 1417
 }
1418 1418
 
1419
+func (container *Container) EnvConfigPath() string {
1420
+	return path.Join(container.root, "config.env")
1421
+}
1422
+
1419 1423
 func (container *Container) lxcConfigPath() string {
1420 1424
 	return path.Join(container.root, "config.lxc")
1421 1425
 }
... ...
@@ -1459,7 +1513,7 @@ func (container *Container) GetSize() (int64, int64) {
1459 1459
 	return sizeRw, sizeRootfs
1460 1460
 }
1461 1461
 
1462
-func (container *Container) Copy(resource string) (Archive, error) {
1462
+func (container *Container) Copy(resource string) (archive.Archive, error) {
1463 1463
 	if err := container.EnsureMounted(); err != nil {
1464 1464
 		return nil, err
1465 1465
 	}
... ...
@@ -1477,7 +1531,7 @@ func (container *Container) Copy(resource string) (Archive, error) {
1477 1477
 		filter = []string{path.Base(basePath)}
1478 1478
 		basePath = path.Dir(basePath)
1479 1479
 	}
1480
-	return TarFilter(basePath, Uncompressed, filter)
1480
+	return archive.TarFilter(basePath, archive.Uncompressed, filter)
1481 1481
 }
1482 1482
 
1483 1483
 // Returns true if the container exposes a certain port
... ...
@@ -40,7 +40,7 @@ func TestIDFormat(t *testing.T) {
40 40
 func TestMultipleAttachRestart(t *testing.T) {
41 41
 	runtime := mkRuntime(t)
42 42
 	defer nuke(runtime)
43
-	container, hostConfig, _ := mkContainer(
43
+	container, _ := mkContainer(
44 44
 		runtime,
45 45
 		[]string{"_", "/bin/sh", "-c", "i=1; while [ $i -le 5 ]; do i=`expr $i + 1`;  echo hello; done"},
46 46
 		t,
... ...
@@ -61,7 +61,7 @@ func TestMultipleAttachRestart(t *testing.T) {
61 61
 	if err != nil {
62 62
 		t.Fatal(err)
63 63
 	}
64
-	if err := container.Start(hostConfig); err != nil {
64
+	if err := container.Start(); err != nil {
65 65
 		t.Fatal(err)
66 66
 	}
67 67
 	l1, err := bufio.NewReader(stdout1).ReadString('\n')
... ...
@@ -102,7 +102,7 @@ func TestMultipleAttachRestart(t *testing.T) {
102 102
 	if err != nil {
103 103
 		t.Fatal(err)
104 104
 	}
105
-	if err := container.Start(hostConfig); err != nil {
105
+	if err := container.Start(); err != nil {
106 106
 		t.Fatal(err)
107 107
 	}
108 108
 
... ...
@@ -136,7 +136,7 @@ func TestDiff(t *testing.T) {
136 136
 	runtime := mkRuntime(t)
137 137
 	defer nuke(runtime)
138 138
 	// Create a container and remove a file
139
-	container1, _, _ := mkContainer(runtime, []string{"_", "/bin/rm", "/etc/passwd"}, t)
139
+	container1, _ := mkContainer(runtime, []string{"_", "/bin/rm", "/etc/passwd"}, t)
140 140
 	defer runtime.Destroy(container1)
141 141
 
142 142
 	// The changelog should be empty and not fail before run. See #1705
... ...
@@ -178,7 +178,7 @@ func TestDiff(t *testing.T) {
178 178
 	}
179 179
 
180 180
 	// Create a new container from the commited image
181
-	container2, _, _ := mkContainer(runtime, []string{img.ID, "cat", "/etc/passwd"}, t)
181
+	container2, _ := mkContainer(runtime, []string{img.ID, "cat", "/etc/passwd"}, t)
182 182
 	defer runtime.Destroy(container2)
183 183
 
184 184
 	if err := container2.Run(); err != nil {
... ...
@@ -197,7 +197,7 @@ func TestDiff(t *testing.T) {
197 197
 	}
198 198
 
199 199
 	// Create a new container
200
-	container3, _, _ := mkContainer(runtime, []string{"_", "rm", "/bin/httpd"}, t)
200
+	container3, _ := mkContainer(runtime, []string{"_", "rm", "/bin/httpd"}, t)
201 201
 	defer runtime.Destroy(container3)
202 202
 
203 203
 	if err := container3.Run(); err != nil {
... ...
@@ -223,7 +223,7 @@ func TestDiff(t *testing.T) {
223 223
 func TestCommitAutoRun(t *testing.T) {
224 224
 	runtime := mkRuntime(t)
225 225
 	defer nuke(runtime)
226
-	container1, _, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t)
226
+	container1, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t)
227 227
 	defer runtime.Destroy(container1)
228 228
 
229 229
 	if container1.State.Running {
... ...
@@ -246,7 +246,7 @@ func TestCommitAutoRun(t *testing.T) {
246 246
 	}
247 247
 
248 248
 	// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
249
-	container2, hostConfig, _ := mkContainer(runtime, []string{img.ID}, t)
249
+	container2, _ := mkContainer(runtime, []string{img.ID}, t)
250 250
 	defer runtime.Destroy(container2)
251 251
 	stdout, err := container2.StdoutPipe()
252 252
 	if err != nil {
... ...
@@ -256,7 +256,7 @@ func TestCommitAutoRun(t *testing.T) {
256 256
 	if err != nil {
257 257
 		t.Fatal(err)
258 258
 	}
259
-	if err := container2.Start(hostConfig); err != nil {
259
+	if err := container2.Start(); err != nil {
260 260
 		t.Fatal(err)
261 261
 	}
262 262
 	container2.Wait()
... ...
@@ -283,7 +283,7 @@ func TestCommitRun(t *testing.T) {
283 283
 	runtime := mkRuntime(t)
284 284
 	defer nuke(runtime)
285 285
 
286
-	container1, hostConfig, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t)
286
+	container1, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "echo hello > /world"}, t)
287 287
 	defer runtime.Destroy(container1)
288 288
 
289 289
 	if container1.State.Running {
... ...
@@ -306,7 +306,7 @@ func TestCommitRun(t *testing.T) {
306 306
 	}
307 307
 
308 308
 	// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
309
-	container2, hostConfig, _ := mkContainer(runtime, []string{img.ID, "cat", "/world"}, t)
309
+	container2, _ := mkContainer(runtime, []string{img.ID, "cat", "/world"}, t)
310 310
 	defer runtime.Destroy(container2)
311 311
 	stdout, err := container2.StdoutPipe()
312 312
 	if err != nil {
... ...
@@ -316,7 +316,7 @@ func TestCommitRun(t *testing.T) {
316 316
 	if err != nil {
317 317
 		t.Fatal(err)
318 318
 	}
319
-	if err := container2.Start(hostConfig); err != nil {
319
+	if err := container2.Start(); err != nil {
320 320
 		t.Fatal(err)
321 321
 	}
322 322
 	container2.Wait()
... ...
@@ -342,7 +342,7 @@ func TestCommitRun(t *testing.T) {
342 342
 func TestStart(t *testing.T) {
343 343
 	runtime := mkRuntime(t)
344 344
 	defer nuke(runtime)
345
-	container, hostConfig, _ := mkContainer(runtime, []string{"-m", "33554432", "-c", "1000", "-i", "_", "/bin/cat"}, t)
345
+	container, _ := mkContainer(runtime, []string{"-m", "33554432", "-c", "1000", "-i", "_", "/bin/cat"}, t)
346 346
 	defer runtime.Destroy(container)
347 347
 
348 348
 	cStdin, err := container.StdinPipe()
... ...
@@ -350,7 +350,7 @@ func TestStart(t *testing.T) {
350 350
 		t.Fatal(err)
351 351
 	}
352 352
 
353
-	if err := container.Start(hostConfig); err != nil {
353
+	if err := container.Start(); err != nil {
354 354
 		t.Fatal(err)
355 355
 	}
356 356
 
... ...
@@ -360,7 +360,7 @@ func TestStart(t *testing.T) {
360 360
 	if !container.State.Running {
361 361
 		t.Errorf("Container should be running")
362 362
 	}
363
-	if err := container.Start(hostConfig); err == nil {
363
+	if err := container.Start(); err == nil {
364 364
 		t.Fatalf("A running container should be able to be started")
365 365
 	}
366 366
 
... ...
@@ -372,7 +372,7 @@ func TestStart(t *testing.T) {
372 372
 func TestRun(t *testing.T) {
373 373
 	runtime := mkRuntime(t)
374 374
 	defer nuke(runtime)
375
-	container, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
375
+	container, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
376 376
 	defer runtime.Destroy(container)
377 377
 
378 378
 	if container.State.Running {
... ...
@@ -452,7 +452,7 @@ func TestKillDifferentUser(t *testing.T) {
452 452
 	if container.State.Running {
453 453
 		t.Errorf("Container shouldn't be running")
454 454
 	}
455
-	if err := container.Start(&HostConfig{}); err != nil {
455
+	if err := container.Start(); err != nil {
456 456
 		t.Fatal(err)
457 457
 	}
458 458
 
... ...
@@ -501,7 +501,8 @@ func TestCreateVolume(t *testing.T) {
501 501
 		t.Fatal(err)
502 502
 	}
503 503
 	defer runtime.Destroy(c)
504
-	if err := c.Start(hc); err != nil {
504
+	c.hostConfig = hc
505
+	if err := c.Start(); err != nil {
505 506
 		t.Fatal(err)
506 507
 	}
507 508
 	c.WaitTimeout(500 * time.Millisecond)
... ...
@@ -525,8 +526,7 @@ func TestKill(t *testing.T) {
525 525
 	if container.State.Running {
526 526
 		t.Errorf("Container shouldn't be running")
527 527
 	}
528
-	hostConfig := &HostConfig{}
529
-	if err := container.Start(hostConfig); err != nil {
528
+	if err := container.Start(); err != nil {
530 529
 		t.Fatal(err)
531 530
 	}
532 531
 
... ...
@@ -642,8 +642,7 @@ func TestRestartStdin(t *testing.T) {
642 642
 	if err != nil {
643 643
 		t.Fatal(err)
644 644
 	}
645
-	hostConfig := &HostConfig{}
646
-	if err := container.Start(hostConfig); err != nil {
645
+	if err := container.Start(); err != nil {
647 646
 		t.Fatal(err)
648 647
 	}
649 648
 	if _, err := io.WriteString(stdin, "hello world"); err != nil {
... ...
@@ -673,7 +672,7 @@ func TestRestartStdin(t *testing.T) {
673 673
 	if err != nil {
674 674
 		t.Fatal(err)
675 675
 	}
676
-	if err := container.Start(hostConfig); err != nil {
676
+	if err := container.Start(); err != nil {
677 677
 		t.Fatal(err)
678 678
 	}
679 679
 	if _, err := io.WriteString(stdin, "hello world #2"); err != nil {
... ...
@@ -850,11 +849,10 @@ func TestMultipleContainers(t *testing.T) {
850 850
 	defer runtime.Destroy(container2)
851 851
 
852 852
 	// Start both containers
853
-	hostConfig := &HostConfig{}
854
-	if err := container1.Start(hostConfig); err != nil {
853
+	if err := container1.Start(); err != nil {
855 854
 		t.Fatal(err)
856 855
 	}
857
-	if err := container2.Start(hostConfig); err != nil {
856
+	if err := container2.Start(); err != nil {
858 857
 		t.Fatal(err)
859 858
 	}
860 859
 
... ...
@@ -904,8 +902,7 @@ func TestStdin(t *testing.T) {
904 904
 	if err != nil {
905 905
 		t.Fatal(err)
906 906
 	}
907
-	hostConfig := &HostConfig{}
908
-	if err := container.Start(hostConfig); err != nil {
907
+	if err := container.Start(); err != nil {
909 908
 		t.Fatal(err)
910 909
 	}
911 910
 	defer stdin.Close()
... ...
@@ -950,8 +947,7 @@ func TestTty(t *testing.T) {
950 950
 	if err != nil {
951 951
 		t.Fatal(err)
952 952
 	}
953
-	hostConfig := &HostConfig{}
954
-	if err := container.Start(hostConfig); err != nil {
953
+	if err := container.Start(); err != nil {
955 954
 		t.Fatal(err)
956 955
 	}
957 956
 	defer stdin.Close()
... ...
@@ -973,14 +969,15 @@ func TestTty(t *testing.T) {
973 973
 }
974 974
 
975 975
 func TestEnv(t *testing.T) {
976
+	os.Setenv("TRUE", "false")
977
+	os.Setenv("TRICKY", "tri\ncky\n")
976 978
 	runtime := mkRuntime(t)
977 979
 	defer nuke(runtime)
978
-	container, _, err := runtime.Create(&Config{
979
-		Image: GetTestImage(runtime).ID,
980
-		Cmd:   []string{"env"},
981
-	},
982
-		"",
983
-	)
980
+	config, _, _, err := ParseRun([]string{"-e=FALSE=true", "-e=TRUE", "-e=TRICKY", GetTestImage(runtime).ID, "env"}, nil)
981
+	if err != nil {
982
+		t.Fatal(err)
983
+	}
984
+	container, _, err := runtime.Create(config, "")
984 985
 	if err != nil {
985 986
 		t.Fatal(err)
986 987
 	}
... ...
@@ -991,8 +988,7 @@ func TestEnv(t *testing.T) {
991 991
 		t.Fatal(err)
992 992
 	}
993 993
 	defer stdout.Close()
994
-	hostConfig := &HostConfig{}
995
-	if err := container.Start(hostConfig); err != nil {
994
+	if err := container.Start(); err != nil {
996 995
 		t.Fatal(err)
997 996
 	}
998 997
 	container.Wait()
... ...
@@ -1010,6 +1006,11 @@ func TestEnv(t *testing.T) {
1010 1010
 		"HOME=/",
1011 1011
 		"container=lxc",
1012 1012
 		"HOSTNAME=" + container.ShortID(),
1013
+		"FALSE=true",
1014
+		"TRUE=false",
1015
+		"TRICKY=tri",
1016
+		"cky",
1017
+		"",
1013 1018
 	}
1014 1019
 	sort.Strings(goodEnv)
1015 1020
 	if len(goodEnv) != len(actualEnv) {
... ...
@@ -1115,7 +1116,7 @@ func TestLXCConfig(t *testing.T) {
1115 1115
 		t.Fatal(err)
1116 1116
 	}
1117 1117
 	defer runtime.Destroy(container)
1118
-	container.generateLXCConfig(nil)
1118
+	container.generateLXCConfig()
1119 1119
 	grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar")
1120 1120
 	grepFile(t, container.lxcConfigPath(),
1121 1121
 		fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
... ...
@@ -1138,7 +1139,7 @@ func TestCustomLxcConfig(t *testing.T) {
1138 1138
 		t.Fatal(err)
1139 1139
 	}
1140 1140
 	defer runtime.Destroy(container)
1141
-	hostConfig := &HostConfig{LxcConf: []KeyValuePair{
1141
+	container.hostConfig = &HostConfig{LxcConf: []KeyValuePair{
1142 1142
 		{
1143 1143
 			Key:   "lxc.utsname",
1144 1144
 			Value: "docker",
... ...
@@ -1149,7 +1150,7 @@ func TestCustomLxcConfig(t *testing.T) {
1149 1149
 		},
1150 1150
 	}}
1151 1151
 
1152
-	container.generateLXCConfig(hostConfig)
1152
+	container.generateLXCConfig()
1153 1153
 	grepFile(t, container.lxcConfigPath(), "lxc.utsname = docker")
1154 1154
 	grepFile(t, container.lxcConfigPath(), "lxc.cgroup.cpuset.cpus = 0,1")
1155 1155
 }
... ...
@@ -1202,8 +1203,7 @@ func BenchmarkRunParallel(b *testing.B) {
1202 1202
 				return
1203 1203
 			}
1204 1204
 			defer runtime.Destroy(container)
1205
-			hostConfig := &HostConfig{}
1206
-			if err := container.Start(hostConfig); err != nil {
1205
+			if err := container.Start(); err != nil {
1207 1206
 				complete <- err
1208 1207
 				return
1209 1208
 			}
... ...
@@ -1247,7 +1247,7 @@ func TestCopyVolumeUidGid(t *testing.T) {
1247 1247
 	defer nuke(r)
1248 1248
 
1249 1249
 	// Add directory not owned by root
1250
-	container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && touch /hello/test.txt && chown daemon.daemon /hello"}, t)
1250
+	container1, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && touch /hello/test.txt && chown daemon.daemon /hello"}, t)
1251 1251
 	defer r.Destroy(container1)
1252 1252
 
1253 1253
 	if container1.State.Running {
... ...
@@ -1284,7 +1284,7 @@ func TestCopyVolumeContent(t *testing.T) {
1284 1284
 	defer nuke(r)
1285 1285
 
1286 1286
 	// Put some content in a directory of a container and commit it
1287
-	container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello/local && echo hello > /hello/local/world"}, t)
1287
+	container1, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello/local && echo hello > /hello/local/world"}, t)
1288 1288
 	defer r.Destroy(container1)
1289 1289
 
1290 1290
 	if container1.State.Running {
... ...
@@ -1521,9 +1521,9 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
1521 1521
 	if err != nil {
1522 1522
 		t.Fatal(err)
1523 1523
 	}
1524
-
1525 1524
 	defer runtime.Destroy(c)
1526
-	if err := c.Start(hc); err != nil {
1525
+	c.hostConfig = hc
1526
+	if err := c.Start(); err != nil {
1527 1527
 		t.Fatal(err)
1528 1528
 	}
1529 1529
 	c.WaitTimeout(500 * time.Millisecond)
... ...
@@ -1652,3 +1652,31 @@ func TestMultipleVolumesFrom(t *testing.T) {
1652 1652
 		t.Fail()
1653 1653
 	}
1654 1654
 }
1655
+
1656
+func TestRestartGhost(t *testing.T) {
1657
+	runtime := mkRuntime(t)
1658
+	defer nuke(runtime)
1659
+
1660
+	container, _, err := runtime.Create(
1661
+		&Config{
1662
+			Image:   GetTestImage(runtime).ID,
1663
+			Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
1664
+			Volumes: map[string]struct{}{"/test": {}},
1665
+		},
1666
+		"",
1667
+	)
1668
+
1669
+	if err != nil {
1670
+		t.Fatal(err)
1671
+	}
1672
+	if err := container.Kill(); err != nil {
1673
+		t.Fatal(err)
1674
+	}
1675
+
1676
+	container.State.Ghost = true
1677
+	_, err = container.Output()
1678
+
1679
+	if err != nil {
1680
+		t.Fatal(err)
1681
+	}
1682
+}
... ...
@@ -426,7 +426,7 @@ _docker_run()
426 426
 
427 427
 _docker_search()
428 428
 {
429
-	COMPREPLY=( $( compgen -W "-notrunc" -- "$cur" ) )
429
+	COMPREPLY=( $( compgen -W "-notrunc" "-stars" "-trusted" -- "$cur" ) )
430 430
 }
431 431
 
432 432
 _docker_start()
433 433
new file mode 100644
... ...
@@ -0,0 +1,11 @@
0
+Desktop Integration
1
+===================
2
+
3
+The ./contrib/desktop-integration contains examples of typical dockerized
4
+desktop applications.
5
+
6
+Examples
7
+========
8
+
9
+* Data container: ./data/Dockerfile creates a data image sharing /data volume
10
+* Firefox: ./firefox/Dockerfile shows a way to dockerize a common multimedia application
0 11
new file mode 100644
... ...
@@ -0,0 +1,38 @@
0
+# VERSION:        0.1
1
+# DESCRIPTION:    Create data image sharing /data volume
2
+# AUTHOR:         Daniel Mizyrycki <daniel@dotcloud.com>
3
+# COMMENTS:
4
+#   This image is used as base for all data containers.
5
+#   /data volume is owned by sysadmin.
6
+# USAGE:
7
+#   # Download data Dockerfile
8
+#   wget http://raw.github.com/dotcloud/docker/master/contrib/desktop-integration/data/Dockerfile
9
+#
10
+#   # Build data image
11
+#   docker build -t data -rm .
12
+#
13
+#   # Create a data container. (eg: firefox-data)
14
+#   docker run -name firefox-data data true
15
+#
16
+#   # List data from it
17
+#   docker run -volumes-from firefox-data busybox ls -al /data
18
+
19
+docker-version 0.6.5
20
+
21
+# Smallest base image, just to launch a container
22
+from busybox
23
+maintainer	Daniel Mizyrycki <daniel@docker.com>
24
+
25
+# Create a regular user
26
+run echo 'sysadmin:x:1000:1000::/data:/bin/sh' >> /etc/passwd
27
+run echo 'sysadmin:x:1000:' >> /etc/group
28
+
29
+# Create directory for that user
30
+run mkdir /data
31
+run chown sysadmin.sysadmin /data
32
+
33
+# Add content to /data. This will keep sysadmin ownership
34
+run touch /data/init_volume
35
+
36
+# Create /data volume
37
+VOLUME /data
0 38
new file mode 100644
... ...
@@ -0,0 +1,49 @@
0
+# VERSION:        0.7
1
+# DESCRIPTION:    Create firefox container with its dependencies
2
+# AUTHOR:         Daniel Mizyrycki <daniel@dotcloud.com>
3
+# COMMENTS:
4
+#   This file describes how to build a Firefox container with all
5
+#   dependencies installed. It uses native X11 unix socket and alsa
6
+#   sound devices. Tested on Debian 7.2
7
+# USAGE:
8
+#   # Download Firefox Dockerfile
9
+#   wget http://raw.github.com/dotcloud/docker/master/contrib/desktop-integration/firefox/Dockerfile
10
+#
11
+#   # Build firefox image
12
+#   docker build -t firefox -rm .
13
+#
14
+#   # Run stateful data-on-host firefox. For ephemeral, remove -v /data/firefox:/data
15
+#   docker run -v /data/firefox:/data -v /tmp/.X11-unix:/tmp/.X11-unix \
16
+#     -v /dev/snd:/dev/snd -lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
17
+#     -e DISPLAY=unix$DISPLAY firefox
18
+#
19
+#   # To run stateful dockerized data containers
20
+#   docker run -volumes-from firefox-data -v /tmp/.X11-unix:/tmp/.X11-unix \
21
+#     -v /dev/snd:/dev/snd -lxc-conf='lxc.cgroup.devices.allow = c 116:* rwm' \
22
+#     -e DISPLAY=unix$DISPLAY firefox
23
+
24
+docker-version 0.6.5
25
+
26
+# Base docker image
27
+from tianon/debian:wheezy
28
+maintainer	Daniel Mizyrycki <daniel@docker.com>
29
+
30
+# Install firefox dependencies
31
+run echo "deb http://ftp.debian.org/debian/ wheezy main contrib" > /etc/apt/sources.list
32
+run apt-get update
33
+run DEBIAN_FRONTEND=noninteractive apt-get install -y libXrender1 libasound2 \
34
+    libdbus-glib-1-2 libgtk2.0-0 libpango1.0-0 libxt6 wget bzip2 sudo
35
+
36
+# Install Firefox
37
+run mkdir /application
38
+run cd /application; wget -O - \
39
+    http://ftp.mozilla.org/pub/mozilla.org/firefox/releases/25.0/linux-x86_64/en-US/firefox-25.0.tar.bz2 | tar jx
40
+
41
+# create sysadmin account
42
+run useradd -m -d /data -p saIVpsc0EVTwA sysadmin
43
+run sed -Ei 's/sudo:x:27:/sudo:x:27:sysadmin/' /etc/group
44
+run sed -Ei 's/(\%sudo\s+ALL=\(ALL\:ALL\) )ALL/\1 NOPASSWD:ALL/' /etc/sudoers
45
+
46
+# Autorun firefox. -no-remote is necessary to create a new container, as firefox
47
+# appears to communicate with itself through X11.
48
+cmd ["/bin/sh", "-c", "/usr/bin/sudo -u sysadmin -H -E /application/firefox/firefox -no-remote"]
... ...
@@ -1,5 +1,5 @@
1 1
 [Unit]
2
-Description=Easily create lightweight, portable, self-sufficient containers from any application!
2
+Description=Docker Application Container Engine 
3 3
 Documentation=http://docs.docker.io
4 4
 Requires=network.target
5 5
 After=multi-user.target
... ...
@@ -6,43 +6,52 @@
6 6
 # Required-Stop:      $syslog $remote_fs
7 7
 # Default-Start:      2 3 4 5
8 8
 # Default-Stop:       0 1 6
9
-# Short-Description:  Linux container runtime
10
-# Description:        Linux container runtime
9
+# Short-Description:  Create lightweight, portable, self-sufficient containers.
10
+# Description:
11
+#  Docker is an open-source project to easily create lightweight, portable,
12
+#  self-sufficient containers from any application. The same container that a
13
+#  developer builds and tests on a laptop can run at scale, in production, on
14
+#  VMs, bare metal, OpenStack clusters, public clouds and more.
11 15
 ### END INIT INFO
12 16
 
13
-DOCKER=/usr/bin/docker
14
-DOCKER_PIDFILE=/var/run/docker.pid
17
+BASE=$(basename $0)
18
+
19
+DOCKER=/usr/bin/$BASE
20
+DOCKER_PIDFILE=/var/run/$BASE.pid
15 21
 DOCKER_OPTS=
16 22
 
17 23
 PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
18 24
 
19
-# Check lxc-docker is present
20
-[ -x $DOCKER ] || (log_failure_msg "docker not present"; exit 1)
21
-
22 25
 # Get lsb functions
23 26
 . /lib/lsb/init-functions
24 27
 
25
-if [ -f /etc/default/lxc ]; then
26
-	. /etc/default/lxc
28
+if [ -f /etc/default/$BASE ]; then
29
+	. /etc/default/$BASE
27 30
 fi
28 31
 
29 32
 if [ "$1" = start ] && which initctl >/dev/null && initctl version | grep -q upstart; then
30 33
 	exit 1
31 34
 fi
32 35
 
33
-check_root_id ()
34
-{
35
-	if [ "$(id -u)" != "0" ]; then
36
-		log_failure_msg "Docker must be run as root"; exit 1
36
+# Check docker is present
37
+if [ ! -x $DOCKER ]; then
38
+	log_failure_msg "$DOCKER not present or not executable"
39
+	exit 1
40
+fi
41
+
42
+fail_unless_root() {
43
+	if [ "$(id -u)" != '0' ]; then
44
+		log_failure_msg "Docker must be run as root"
45
+		exit 1
37 46
 	fi
38 47
 }
39 48
 
40 49
 case "$1" in
41 50
 	start)
42
-		check_root_id || exit 1
43
-		log_begin_msg "Starting Docker"
51
+		fail_unless_root
52
+		log_begin_msg "Starting Docker: $BASE"
44 53
 		mount | grep cgroup >/dev/null || mount -t cgroup none /sys/fs/cgroup 2>/dev/null
45
-		start-stop-daemon --start --background $NO_CLOSE \
54
+		start-stop-daemon --start --background \
46 55
 			--exec "$DOCKER" \
47 56
 			--pidfile "$DOCKER_PIDFILE" \
48 57
 			-- -d -p "$DOCKER_PIDFILE" \
... ...
@@ -51,15 +60,15 @@ case "$1" in
51 51
 		;;
52 52
 
53 53
 	stop)
54
-		check_root_id || exit 1
55
-		log_begin_msg "Stopping Docker"
54
+		fail_unless_root
55
+		log_begin_msg "Stopping Docker: $BASE"
56 56
 		start-stop-daemon --stop \
57 57
 			--pidfile "$DOCKER_PIDFILE"
58 58
 		log_end_msg $?
59 59
 		;;
60 60
 
61 61
 	restart)
62
-		check_root_id || exit 1
62
+		fail_unless_root
63 63
 		docker_pid=`cat "$DOCKER_PIDFILE" 2>/dev/null`
64 64
 		[ -n "$docker_pid" ] \
65 65
 			&& ps -p $docker_pid > /dev/null 2>&1 \
... ...
@@ -68,7 +77,7 @@ case "$1" in
68 68
 		;;
69 69
 
70 70
 	force-reload)
71
-		check_root_id || exit 1
71
+		fail_unless_root
72 72
 		$0 restart
73 73
 		;;
74 74
 
... ...
@@ -6,14 +6,10 @@ import (
6 6
 	"github.com/dotcloud/docker"
7 7
 	"github.com/dotcloud/docker/sysinit"
8 8
 	"github.com/dotcloud/docker/utils"
9
-	"io/ioutil"
9
+	"github.com/dotcloud/docker/engine"
10 10
 	"log"
11
-	"net"
12 11
 	"os"
13
-	"os/signal"
14
-	"strconv"
15 12
 	"strings"
16
-	"syscall"
17 13
 )
18 14
 
19 15
 var (
... ...
@@ -34,7 +30,7 @@ func main() {
34 34
 	flAutoRestart := flag.Bool("r", true, "Restart previously running containers")
35 35
 	bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge. Use 'none' to disable container networking")
36 36
 	pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
37
-	flGraphPath := flag.String("g", "/var/lib/docker", "Path to graph storage base dir.")
37
+	flRoot := flag.String("g", "/var/lib/docker", "Path to use as the root of the docker runtime.")
38 38
 	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
39 39
 	flDns := flag.String("dns", "", "Set custom dns servers")
40 40
 	flHosts := utils.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
... ...
@@ -61,10 +57,6 @@ func main() {
61 61
 		}
62 62
 	}
63 63
 
64
-	bridge := docker.DefaultNetworkBridge
65
-	if *bridgeName != "" {
66
-		bridge = *bridgeName
67
-	}
68 64
 	if *flDebug {
69 65
 		os.Setenv("DEBUG", "1")
70 66
 	}
... ...
@@ -75,26 +67,22 @@ func main() {
75 75
 			flag.Usage()
76 76
 			return
77 77
 		}
78
-		var dns []string
79
-		if *flDns != "" {
80
-			dns = []string{*flDns}
81
-		}
82
-
83
-		ip := net.ParseIP(*flDefaultIp)
84
-
85
-		config := &docker.DaemonConfig{
86
-			Pidfile:                     *pidfile,
87
-			GraphPath:                   *flGraphPath,
88
-			AutoRestart:                 *flAutoRestart,
89
-			EnableCors:                  *flEnableCors,
90
-			Dns:                         dns,
91
-			EnableIptables:              *flEnableIptables,
92
-			BridgeIface:                 bridge,
93
-			ProtoAddresses:              flHosts,
94
-			DefaultIp:                   ip,
95
-			InterContainerCommunication: *flInterContainerComm,
78
+		eng, err := engine.New(*flRoot)
79
+		if err != nil {
80
+			log.Fatal(err)
96 81
 		}
97
-		if err := daemon(config); err != nil {
82
+		job := eng.Job("serveapi")
83
+		job.Setenv("Pidfile", *pidfile)
84
+		job.Setenv("Root", *flRoot)
85
+		job.SetenvBool("AutoRestart", *flAutoRestart)
86
+		job.SetenvBool("EnableCors", *flEnableCors)
87
+		job.Setenv("Dns", *flDns)
88
+		job.SetenvBool("EnableIptables", *flEnableIptables)
89
+		job.Setenv("BridgeIface", *bridgeName)
90
+		job.SetenvList("ProtoAddresses", flHosts)
91
+		job.Setenv("DefaultIp", *flDefaultIp)
92
+		job.SetenvBool("InterContainerCommunication", *flInterContainerComm)
93
+		if err := job.Run(); err != nil {
98 94
 			log.Fatal(err)
99 95
 		}
100 96
 	} else {
... ...
@@ -114,79 +102,3 @@ func main() {
114 114
 func showVersion() {
115 115
 	fmt.Printf("Docker version %s, build %s\n", VERSION, GITCOMMIT)
116 116
 }
117
-
118
-func createPidFile(pidfile string) error {
119
-	if pidString, err := ioutil.ReadFile(pidfile); err == nil {
120
-		pid, err := strconv.Atoi(string(pidString))
121
-		if err == nil {
122
-			if _, err := os.Stat(fmt.Sprintf("/proc/%d/", pid)); err == nil {
123
-				return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile)
124
-			}
125
-		}
126
-	}
127
-
128
-	file, err := os.Create(pidfile)
129
-	if err != nil {
130
-		return err
131
-	}
132
-
133
-	defer file.Close()
134
-
135
-	_, err = fmt.Fprintf(file, "%d", os.Getpid())
136
-	return err
137
-}
138
-
139
-func removePidFile(pidfile string) {
140
-	if err := os.Remove(pidfile); err != nil {
141
-		log.Printf("Error removing %s: %s", pidfile, err)
142
-	}
143
-}
144
-
145
-func daemon(config *docker.DaemonConfig) error {
146
-	if err := createPidFile(config.Pidfile); err != nil {
147
-		log.Fatal(err)
148
-	}
149
-	defer removePidFile(config.Pidfile)
150
-
151
-	server, err := docker.NewServer(config)
152
-	if err != nil {
153
-		return err
154
-	}
155
-	defer server.Close()
156
-
157
-	c := make(chan os.Signal, 1)
158
-	signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM))
159
-	go func() {
160
-		sig := <-c
161
-		log.Printf("Received signal '%v', exiting\n", sig)
162
-		server.Close()
163
-		removePidFile(config.Pidfile)
164
-		os.Exit(0)
165
-	}()
166
-
167
-	chErrors := make(chan error, len(config.ProtoAddresses))
168
-	for _, protoAddr := range config.ProtoAddresses {
169
-		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
170
-		if protoAddrParts[0] == "unix" {
171
-			syscall.Unlink(protoAddrParts[1])
172
-		} else if protoAddrParts[0] == "tcp" {
173
-			if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") {
174
-				log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
175
-			}
176
-		} else {
177
-			server.Close()
178
-			removePidFile(config.Pidfile)
179
-			log.Fatal("Invalid protocol format.")
180
-		}
181
-		go func() {
182
-			chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true)
183
-		}()
184
-	}
185
-	for i := 0; i < len(config.ProtoAddresses); i += 1 {
186
-		err := <-chErrors
187
-		if err != nil {
188
-			return err
189
-		}
190
-	}
191
-	return nil
192
-}
... ...
@@ -943,36 +943,36 @@ Build an image from Dockerfile via stdin
943 943
 
944 944
 .. http:post:: /build
945 945
 
946
-   Build an image from Dockerfile via stdin
946
+    Build an image from Dockerfile via stdin
947 947
 
948
-   **Example request**:
948
+    **Example request**:
949 949
 
950
-   .. sourcecode:: http
950
+    .. sourcecode:: http
951 951
 
952
-      POST /build HTTP/1.1
952
+        POST /build HTTP/1.1
953 953
 
954
-      {{ STREAM }}
954
+        {{ STREAM }}
955 955
 
956
-   **Example response**:
956
+    **Example response**:
957 957
 
958
-   .. sourcecode:: http
958
+    .. sourcecode:: http
959 959
 
960
-      HTTP/1.1 200 OK
960
+        HTTP/1.1 200 OK
961 961
 
962
-      {{ STREAM }}
962
+        {{ STREAM }}
963 963
 
964 964
 
965
-       The stream must be a tar archive compressed with one of the following algorithms:
966
-       identity (no compression), gzip, bzip2, xz. The archive must include a file called
967
-       `Dockerfile` at its root. It may include any number of other files, which will be
968
-       accessible in the build context (See the ADD build command).
965
+    The stream must be a tar archive compressed with one of the following algorithms:
966
+    identity (no compression), gzip, bzip2, xz. The archive must include a file called
967
+    `Dockerfile` at its root. It may include any number of other files, which will be
968
+    accessible in the build context (See the ADD build command).
969 969
 
970
-       The Content-type header should be set to "application/tar".
970
+    The Content-type header should be set to "application/tar".
971 971
 
972
-	:query t: repository name (and optionally a tag) to be applied to the resulting image in case of success
973
-	:query q: suppress verbose build output
972
+    :query t: repository name (and optionally a tag) to be applied to the resulting image in case of success
973
+    :query q: suppress verbose build output
974 974
     :query nocache: do not use the cache when building the image
975
-	:statuscode 200: no error
975
+    :statuscode 200: no error
976 976
     :statuscode 500: server error
977 977
 
978 978
 
... ...
@@ -1069,8 +1069,8 @@ Show the docker version information
1069 1069
 		"GoVersion":"go1.0.3"
1070 1070
 	   }
1071 1071
 
1072
-        :statuscode 200: no error
1073
-	:statuscode 500: server error
1072
+    :statuscode 200: no error
1073
+    :statuscode 500: server error
1074 1074
 
1075 1075
 
1076 1076
 Create a new image from a container's changes
... ...
@@ -995,36 +995,36 @@ Build an image from Dockerfile via stdin
995 995
 
996 996
 .. http:post:: /build
997 997
 
998
-   Build an image from Dockerfile via stdin
998
+    Build an image from Dockerfile via stdin
999 999
 
1000
-   **Example request**:
1001
-
1002
-   .. sourcecode:: http
1000
+    **Example request**:
1003 1001
 
1004
-      POST /build HTTP/1.1
1002
+    .. sourcecode:: http
1005 1003
 
1006
-      {{ STREAM }}
1004
+        POST /build HTTP/1.1
1007 1005
 
1008
-   **Example response**:
1006
+        {{ STREAM }}
1009 1007
 
1010
-   .. sourcecode:: http
1008
+    **Example response**:
1011 1009
 
1012
-      HTTP/1.1 200 OK
1010
+    .. sourcecode:: http
1013 1011
 
1014
-      {{ STREAM }}
1012
+        HTTP/1.1 200 OK
1015 1013
 
1014
+        {{ STREAM }}
1016 1015
 
1017
-       The stream must be a tar archive compressed with one of the following algorithms:
1018
-       identity (no compression), gzip, bzip2, xz. The archive must include a file called
1019
-       `Dockerfile` at its root. It may include any number of other files, which will be
1020
-       accessible in the build context (See the ADD build command).
1021 1016
 
1022
-       The Content-type header should be set to "application/tar".
1017
+    The stream must be a tar archive compressed with one of the following algorithms:
1018
+    identity (no compression), gzip, bzip2, xz. The archive must include a file called
1019
+    `Dockerfile` at its root. It may include any number of other files, which will be
1020
+    accessible in the build context (See the ADD build command).
1021
+    
1022
+    The Content-type header should be set to "application/tar".
1023 1023
 
1024
-	:query t: repository name (and optionally a tag) to be applied to the resulting image in case of success
1025
-	:query q: suppress verbose build output
1024
+    :query t: repository name (and optionally a tag) to be applied to the resulting image in case of success
1025
+    :query q: suppress verbose build output
1026 1026
     :query nocache: do not use the cache when building the image
1027
-	:statuscode 200: no error
1027
+    :statuscode 200: no error
1028 1028
     :statuscode 500: server error
1029 1029
 
1030 1030
 
... ...
@@ -41,7 +41,7 @@ To stop a container, use ``docker stop``
41 41
 To kill the container, use ``docker kill``
42 42
 
43 43
 .. _cli_attach_examples:
44
- 
44
+
45 45
 Examples:
46 46
 ~~~~~~~~~
47 47
 
... ...
@@ -55,8 +55,8 @@ Examples:
55 55
      Mem:    373572k total,   355560k used,    18012k free,    27872k buffers
56 56
      Swap:   786428k total,        0k used,   786428k free,   221740k cached
57 57
 
58
-     PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
59
-      1 root      20   0 17200 1116  912 R    0  0.3   0:00.03 top                
58
+     PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
59
+      1 root      20   0 17200 1116  912 R    0  0.3   0:00.03 top
60 60
 
61 61
       top - 02:05:55 up  3:05,  0 users,  load average: 0.01, 0.02, 0.05
62 62
       Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
... ...
@@ -64,8 +64,8 @@ Examples:
64 64
       Mem:    373572k total,   355244k used,    18328k free,    27872k buffers
65 65
       Swap:   786428k total,        0k used,   786428k free,   221776k cached
66 66
 
67
-        PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
68
-	    1 root      20   0 17208 1144  932 R    0  0.3   0:00.03 top                
67
+        PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
68
+	    1 root      20   0 17208 1144  932 R    0  0.3   0:00.03 top
69 69
 
70 70
 
71 71
       top - 02:05:58 up  3:06,  0 users,  load average: 0.01, 0.02, 0.05
... ...
@@ -74,9 +74,9 @@ Examples:
74 74
       Mem:    373572k total,   355780k used,    17792k free,    27880k buffers
75 75
       Swap:   786428k total,        0k used,   786428k free,   221776k cached
76 76
 
77
-      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
78
-           1 root      20   0 17208 1144  932 R    0  0.3   0:00.03 top                
79
-     ^C$ 
77
+      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
78
+           1 root      20   0 17208 1144  932 R    0  0.3   0:00.03 top
79
+     ^C$
80 80
      $ sudo docker stop $ID
81 81
 
82 82
 .. _cli_build:
... ...
@@ -133,7 +133,6 @@ to the ``docker`` daemon.  ``ADD`` doesn't work when running in this
133 133
 mode because the absence of the context provides no source files to
134 134
 copy to the container.
135 135
 
136
-
137 136
 .. code-block:: bash
138 137
 
139 138
     sudo docker build github.com/creack/docker-firefox
... ...
@@ -151,16 +150,35 @@ by using the ``git://`` schema.
151 151
 
152 152
 ::
153 153
 
154
-    Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY [TAG]]
154
+    Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
155 155
 
156 156
     Create a new image from a container's changes
157 157
 
158 158
       -m="": Commit message
159 159
       -author="": Author (eg. "John Hannibal Smith <hannibal@a-team.com>"
160
-      -run="": Configuration to be applied when the image is launched with `docker run`. 
160
+      -run="": Configuration to be applied when the image is launched with `docker run`.
161 161
                (ex: '{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')
162 162
 
163
-Full -run example (multiline is ok within a single quote ``'``)
163
+Simple commit of an existing container
164
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
165
+
166
+.. code-block:: bash
167
+
168
+	$ docker ps
169
+	ID                  IMAGE               COMMAND             CREATED             STATUS              PORTS
170
+	c3f279d17e0a        ubuntu:12.04        /bin/bash           7 days ago          Up 25 hours                             
171
+	197387f1b436        ubuntu:12.04        /bin/bash           7 days ago          Up 25 hours                             
172
+	$ docker commit c3f279d17e0a  SvenDowideit/testimage:version3
173
+	f5283438590d
174
+	$ docker images | head
175
+	REPOSITORY                        TAG                 ID                  CREATED             SIZE
176
+	SvenDowideit/testimage            version3            f5283438590d        16 seconds ago      204.2 MB (virtual 335.7 MB)
177
+	S
178
+
179
+Full -run example
180
+.................
181
+
182
+(multiline is ok within a single quote ``'``)
164 183
 
165 184
 ::
166 185
 
... ...
@@ -239,7 +257,7 @@ Shell 1: Listening for events
239 239
 .............................
240 240
 
241 241
 .. code-block:: bash
242
-    
242
+
243 243
     $ sudo docker events
244 244
 
245 245
 Shell 2: Start and Stop a Container
... ...
@@ -282,6 +300,9 @@ Shell 1: (Again .. now showing events)
282 282
 
283 283
     Show the history of an image
284 284
 
285
+      -notrunc=false: Don't truncate output
286
+      -q=false: only show numeric IDs
287
+
285 288
 .. _cli_images:
286 289
 
287 290
 ``images``
... ...
@@ -314,7 +335,7 @@ Displaying images visually
314 314
 
315 315
 ::
316 316
 
317
-    Usage: docker import URL|- [REPOSITORY [TAG]]
317
+    Usage: docker import URL|- [REPOSITORY[:TAG]]
318 318
 
319 319
     Create a new filesystem image from the contents of a tarball
320 320
 
... ...
@@ -330,14 +351,16 @@ Examples
330 330
 Import from a remote location
331 331
 .............................
332 332
 
333
-``$ sudo docker import http://example.com/exampleimage.tgz exampleimagerepo``
333
+This will create a new untagged image.
334
+
335
+``$ sudo docker import http://example.com/exampleimage.tgz``
334 336
 
335 337
 Import from a local file
336 338
 ........................
337 339
 
338 340
 Import to docker via pipe and standard in
339 341
 
340
-``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal``
342
+``$ cat exampleimage.tgz | sudo docker import - exampleimagelocal:new``
341 343
 
342 344
 Import from a local directory
343 345
 .............................
... ...
@@ -401,7 +424,9 @@ Insert file from github
401 401
 
402 402
     Usage: docker kill CONTAINER [CONTAINER...]
403 403
 
404
-    Kill a running container
404
+    Kill a running container (Send SIGKILL)
405
+
406
+The main process inside the container will be sent SIGKILL.
405 407
 
406 408
 .. _cli_login:
407 409
 
... ...
@@ -510,7 +535,7 @@ Insert file from github
510 510
 
511 511
     Remove one or more containers
512 512
         -link="": Remove the link instead of the actual container
513
- 
513
+
514 514
 
515 515
 Examples:
516 516
 ~~~~~~~~~
... ...
@@ -579,6 +604,7 @@ network communication.
579 579
       -expose=[]: Expose a port from the container without publishing it to your host
580 580
       -link="": Add link to another container (name:alias)
581 581
       -name="": Assign the specified name to the container. If no name is specific docker will generate a random name
582
+      -P=false: Publish all exposed ports to the host interfaces
582 583
 
583 584
 Examples
584 585
 --------
... ...
@@ -615,58 +641,51 @@ use-cases, like running Docker within Docker.
615 615
 
616 616
    docker  run -w /path/to/dir/ -i -t  ubuntu pwd
617 617
 
618
-The ``-w`` lets the command being executed inside directory given, 
619
-here /path/to/dir/. If the path does not exists it is created inside the 
618
+The ``-w`` lets the command being executed inside directory given,
619
+here /path/to/dir/. If the path does not exists it is created inside the
620 620
 container.
621 621
 
622 622
 .. code-block:: bash
623 623
 
624 624
    docker  run  -v `pwd`:`pwd` -w `pwd` -i -t  ubuntu pwd
625 625
 
626
-The ``-v`` flag mounts the current working directory into the container. 
627
-The ``-w`` lets the command being executed inside the current 
626
+The ``-v`` flag mounts the current working directory into the container.
627
+The ``-w`` lets the command being executed inside the current
628 628
 working directory, by changing into the directory to the value
629 629
 returned by ``pwd``. So this combination executes the command
630 630
 using the container, but inside the current working directory.
631 631
 
632 632
 .. code-block:: bash
633 633
 
634
-    docker run -p 127.0.0.0::80 ubuntu bash
635
-
636
-This the ``-p`` flag now allows you to bind a port to a specific
637
-interface of the host machine.  In this example port ``80`` of the 
638
-container will have a dynamically allocated port bound to 127.0.0.1 
639
-of the host.
640
-
641
-.. code-block:: bash
642
-
643
-    docker run -p 127.0.0.1:80:80 ubuntu bash
634
+    docker run -p 127.0.0.1:80:8080 ubuntu bash
644 635
 
645
-This will bind port ``80`` of the container to port ``80`` on 127.0.0.1 of your
646
-host machine.
636
+This binds port ``8080`` of the container to port ``80`` on 127.0.0.1 of the
637
+host machine. :ref:`port_redirection` explains in detail how to manipulate ports
638
+in Docker.
647 639
 
648 640
 .. code-block:: bash
649 641
 
650 642
     docker run -expose 80 ubuntu bash
651 643
 
652
-This will expose port ``80`` of the container for use within a link
653
-without publishing the port to the host system's interfaces.  
644
+This exposes port ``80`` of the container for use within a link without
645
+publishing the port to the host system's interfaces. :ref:`port_redirection`
646
+explains in detail how to manipulate ports in Docker.
654 647
 
655 648
 .. code-block:: bash
656 649
 
657 650
     docker run -name console -t -i ubuntu bash
658 651
 
659
-This will create and run a new container with the container name 
652
+This will create and run a new container with the container name
660 653
 being ``console``.
661 654
 
662 655
 .. code-block:: bash
663 656
 
664 657
     docker run -link /redis:redis -name console ubuntu bash
665 658
 
666
-The ``-link`` flag will link the container named ``/redis`` into the 
659
+The ``-link`` flag will link the container named ``/redis`` into the
667 660
 newly created container with the alias ``redis``.  The new container
668 661
 can access the network and environment of the redis container via
669
-environment variables.  The ``-name`` flag will assign the name ``console`` 
662
+environment variables.  The ``-name`` flag will assign the name ``console``
670 663
 to the newly created container.
671 664
 
672 665
 .. _cli_search:
... ...
@@ -678,8 +697,11 @@ to the newly created container.
678 678
 
679 679
     Usage: docker search TERM
680 680
 
681
-    Searches for the TERM parameter on the Docker index and prints out
682
-    a list of repositories that match.
681
+    Search the docker index for images
682
+
683
+     -notrunc=false: Don't truncate output
684
+     -stars=0: Only displays with at least xxx stars
685
+     -trusted=false: Only show trusted builds
683 686
 
684 687
 .. _cli_start:
685 688
 
... ...
@@ -704,10 +726,12 @@ to the newly created container.
704 704
 
705 705
     Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
706 706
 
707
-    Stop a running container
707
+    Stop a running container (Send SIGTERM, and then SIGKILL after grace period)
708 708
 
709 709
       -t=10: Number of seconds to wait for the container to stop before killing it.
710 710
 
711
+The main process inside the container will receive SIGTERM, and after a grace period, SIGKILL
712
+
711 713
 .. _cli_tag:
712 714
 
713 715
 ``tag``
... ...
@@ -715,7 +739,7 @@ to the newly created container.
715 715
 
716 716
 ::
717 717
 
718
-    Usage: docker tag [OPTIONS] IMAGE REPOSITORY [TAG]
718
+    Usage: docker tag [OPTIONS] IMAGE REPOSITORY[:TAG]
719 719
 
720 720
     Tag an image into a repository
721 721
 
... ...
@@ -728,7 +752,7 @@ to the newly created container.
728 728
 
729 729
 ::
730 730
 
731
-    Usage: docker top CONTAINER
731
+    Usage: docker top CONTAINER [ps OPTIONS]
732 732
 
733 733
     Lookup the running processes of a container
734 734
 
... ...
@@ -750,6 +774,3 @@ Show the version of the docker client, daemon, and latest released version.
750 750
     Usage: docker wait [OPTIONS] NAME
751 751
 
752 752
     Block until a container stops, then print its exit code.
753
-
754
-
755
-
... ...
@@ -56,7 +56,7 @@ To create the Docker binary, run this command:
56 56
 
57 57
 .. code-block:: bash
58 58
 
59
-	sudo docker run -lxc-conf=lxc.aa_profile=unconfined -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh binary
59
+	sudo docker run -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh binary
60 60
 
61 61
 This will create the Docker binary in ``./bundles/<version>-dev/binary/``
62 62
 
... ...
@@ -64,19 +64,14 @@ This will create the Docker binary in ``./bundles/<version>-dev/binary/``
64 64
 Step 5: Run the Tests
65 65
 ---------------------
66 66
 
67
-To run the Docker test cases you first need to disable `AppArmor <https://wiki.ubuntu.com/AppArmor>`_ using the following commands
68
-
69
-.. code-block:: bash
70
-
71
-	sudo /etc/init.d/apparmor stop
72
-	sudo /etc/init.d/apparmor teardown
73
-
74 67
 To execute the test cases, run this command:
75 68
 
76 69
 .. code-block:: bash
77 70
 
78
-	sudo docker run -lxc-conf=lxc.aa_profile=unconfined -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh test
71
+	sudo docker run -privileged -v `pwd`:/go/src/github.com/dotcloud/docker docker hack/make.sh test
72
+
79 73
 
74
+Note: if you're running the tests in vagrant, you need to specify a dns entry in the command: `-dns 8.8.8.8`
80 75
 
81 76
 If the test are successful then the tail of the output should look something like this
82 77
 
... ...
@@ -10,7 +10,7 @@ CouchDB Service
10 10
 .. include:: example_header.inc
11 11
 
12 12
 Here's an example of using data volumes to share the same data between
13
-2 CouchDB containers.  This could be used for hot upgrades, testing
13
+two CouchDB containers.  This could be used for hot upgrades, testing
14 14
 different versions of CouchDB on the same data, etc.
15 15
 
16 16
 Create first database
... ...
@@ -25,8 +25,8 @@ Note that we're marking ``/var/lib/couchdb`` as a data volume.
25 25
 Add data to the first database
26 26
 ------------------------------
27 27
 
28
-We're assuming your docker host is reachable at `localhost`. If not,
29
-replace `localhost` with the public IP of your docker host.
28
+We're assuming your Docker host is reachable at ``localhost``. If not,
29
+replace ``localhost`` with the public IP of your Docker host.
30 30
 
31 31
 .. code-block:: bash
32 32
 
... ...
@@ -37,7 +37,7 @@ replace `localhost` with the public IP of your docker host.
37 37
 Create second database
38 38
 ----------------------
39 39
 
40
-This time, we're requesting shared access to $COUCH1's volumes.
40
+This time, we're requesting shared access to ``$COUCH1``'s volumes.
41 41
 
42 42
 .. code-block:: bash
43 43
 
... ...
@@ -52,5 +52,5 @@ Browse data on the second database
52 52
     URL="http://$HOST:$(sudo docker port $COUCH2 5984)/_utils/"
53 53
     echo "Navigate to $URL in your browser. You should see the same data as in the first database"'!'
54 54
 
55
-Congratulations, you are running 2 Couchdb containers, completely
55
+Congratulations, you are now running two Couchdb containers, completely
56 56
 isolated from each other *except* for their data.
... ...
@@ -12,16 +12,16 @@ Hello World
12 12
 Running the Examples
13 13
 ====================
14 14
 
15
-All the examples assume your machine is running the docker daemon. To
16
-run the docker daemon in the background, simply type:
15
+All the examples assume your machine is running the ``docker`` daemon. To
16
+run the ``docker`` daemon in the background, simply type:
17 17
 
18 18
 .. code-block:: bash
19 19
 
20 20
    sudo docker -d &
21 21
 
22
-Now you can run docker in client mode: by default all commands will be
22
+Now you can run Docker in client mode: by default all commands will be
23 23
 forwarded to the ``docker`` daemon via a protected Unix socket, so you
24
-must run as root.
24
+must run as the ``root`` or via the ``sudo`` command.
25 25
 
26 26
 .. code-block:: bash
27 27
 
... ...
@@ -38,23 +38,24 @@ Hello World
38 38
 
39 39
 This is the most basic example available for using Docker.
40 40
 
41
-Download the base image (named "ubuntu"):
41
+Download the base image which is named ``ubuntu``:
42 42
 
43 43
 .. code-block:: bash
44 44
 
45 45
     # Download an ubuntu image
46 46
     sudo docker pull ubuntu
47 47
 
48
-Alternatively to the *ubuntu* image, you can select *busybox*, a bare
48
+Alternatively to the ``ubuntu`` image, you can select ``busybox``, a bare
49 49
 minimal Linux system. The images are retrieved from the Docker
50 50
 repository.
51 51
 
52 52
 
53 53
 .. code-block:: bash
54 54
 
55
-    #run a simple echo command, that will echo hello world back to the console over standard out.
56 55
     sudo docker run ubuntu /bin/echo hello world
57 56
 
57
+This command will run a simple ``echo`` command, that will echo ``hello world`` back to the console over standard out.
58
+
58 59
 **Explanation:**
59 60
 
60 61
 - **"sudo"** execute the following commands as user *root* 
... ...
@@ -100,9 +101,9 @@ we stop it.
100 100
     CONTAINER_ID=$(sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done")
101 101
 
102 102
 We are going to run a simple hello world daemon in a new container
103
-made from the *ubuntu* image.
103
+made from the ``ubuntu`` image.
104 104
 
105
-- **"docker run -d "** run a command in a new container. We pass "-d"
105
+- **"sudo docker run -d "** run a command in a new container. We pass "-d"
106 106
   so it runs as a daemon.
107 107
 - **"ubuntu"** is the image we want to run the command inside of.
108 108
 - **"/bin/sh -c"** is the command we want to run in the container
... ...
@@ -10,7 +10,7 @@ Examples
10 10
 
11 11
 Here are some examples of how to use Docker to create running
12 12
 processes, starting from a very simple *Hello World* and progressing
13
-to more substantial services like you might find in production.
13
+to more substantial services like those which you might find in production.
14 14
 
15 15
 .. toctree::
16 16
    :maxdepth: 1
... ...
@@ -9,12 +9,14 @@ Linking Redis
9 9
 
10 10
 .. include:: example_header.inc
11 11
 
12
-Building a redis container to link as a child of our web application.
12
+Building a Redis container to link as a child of our web application.
13 13
 
14
-Building the redis container
14
+Building the Redis container
15 15
 ----------------------------
16 16
 
17
-Lets build a redis image with the following Dockerfile.
17
+Lets build a Redis image with the following Dockerfile.
18
+
19
+First checkout the Redis source code.
18 20
 
19 21
 .. code-block:: bash
20 22
 
... ...
@@ -22,7 +24,10 @@ Lets build a redis image with the following Dockerfile.
22 22
     cd redis
23 23
     git checkout 2.6
24 24
 
25
-    # Save this Dockerfile to the root of the redis repository.  
25
+
26
+Now let's create a Dockerfile in the root of the Redis repository.
27
+
28
+.. code-block:: bash
26 29
 
27 30
     # Build redis from source
28 31
     # Make sure you have the redis source code checked out in
... ...
@@ -51,37 +56,37 @@ Lets build a redis image with the following Dockerfile.
51 51
 
52 52
 
53 53
 We need to ``EXPOSE`` the default port of 6379 so that our link knows what ports 
54
-to connect to our redis container on.  If you do not expose any ports for the
54
+to connect to our Redis container on.  If you do not expose any ports for the
55 55
 image then docker will not be able to establish the link between containers.
56 56
 
57 57
 
58
-Run the redis container
58
+Run the Redis container
59 59
 -----------------------
60 60
 
61 61
 .. code-block:: bash
62
-    
63
-    docker run -d -e PASSWORD=docker -name redis redis-2.6 --requirepass docker
64
- 
65
-This will run our redis container wit the password docker 
62
+
63
+    sudo docker run -d -e PASSWORD=docker -name redis redis-2.6 --requirepass docker
64
+
65
+This will run our Redis container with the password docker 
66 66
 to secure our service.  By specifying the ``-name`` flag on run 
67
-we will assign the name ``redis`` to this container.  If we do not specify a name  for 
67
+we will assign the name ``redis`` to this container.  If we do not specify a name for 
68 68
 our container via the ``-name`` flag docker will automatically generate a name for us.
69 69
 We can issue all the commands that you would expect; start, stop, attach, using the name for our container.
70 70
 The name also allows us to link other containers into this one.
71 71
 
72
-Linking redis as a child
72
+Linking Redis as a child
73 73
 ------------------------
74 74
 
75
-Next we can start a new web application that has a dependency on redis and apply a link 
76
-to connect both containers.  If you noticed when running our redis server we did not use
77
-the ``-p`` flag to publish the redis port to the host system.  Redis exposed port 6379 via the Dockerfile 
75
+Next we can start a new web application that has a dependency on Redis and apply a link 
76
+to connect both containers.  If you noticed when running our Redis server we did not use
77
+the ``-p`` flag to publish the Redis port to the host system.  Redis exposed port 6379 via the Dockerfile 
78 78
 and this is all we need to establish a link.
79 79
 
80
-Now lets start our web application with a link into redis.
80
+Now let's start our web application with a link into Redis.
81 81
 
82 82
 .. code-block:: bash
83
-   
84
-    docker run -t -i -link redis:db -name webapp ubuntu bash
83
+
84
+    sudo docker run -t -i -link redis:db -name webapp ubuntu bash
85 85
 
86 86
     root@4c01db0b339c:/# env
87 87
 
... ...
@@ -95,7 +100,7 @@ Now lets start our web application with a link into redis.
95 95
     DB_PORT_6379_TCP_PORT=6379
96 96
     PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
97 97
     PWD=/
98
-    DB_ENV_PASSWORD=dockerpass
98
+    DB_ENV_PASSWORD=docker
99 99
     SHLVL=1
100 100
     HOME=/
101 101
     container=lxc
... ...
@@ -105,7 +110,7 @@ Now lets start our web application with a link into redis.
105 105
 
106 106
 When we inspect the environment of the linked container we can see a few extra environment 
107 107
 variables have been added.  When you specified ``-link redis:db`` you are telling docker
108
-to link the container named ``redis`` into this new container with the alias ``db``.  
108
+to link the container named ``redis`` into this new container with the alias ``db``.
109 109
 Environment variables are prefixed with the alias so that the parent container can access
110 110
 network and environment information from the containers that are linked into it.
111 111
 
... ...
@@ -124,9 +129,9 @@ network and environment information from the containers that are linked into it.
124 124
     DB_PORT_6379_TCP_PORT=6379
125 125
 
126 126
     # Get environment variables of the container 
127
-    DB_ENV_PASSWORD=dockerpass
127
+    DB_ENV_PASSWORD=docker
128 128
 
129 129
 
130 130
 Accessing the network information along with the environment of the child container allows
131
-us to easily connect to the redis service on the specific ip and port and use the password
131
+us to easily connect to the Redis service on the specific IP and port and use the password
132 132
 specified in the environment.
... ...
@@ -10,8 +10,8 @@ Building an Image with MongoDB
10 10
 .. include:: example_header.inc
11 11
 
12 12
 The goal of this example is to show how you can build your own
13
-docker images with MongoDB preinstalled. We will do that by
14
-constructing a Dockerfile that downloads a base image, adds an
13
+Docker images with MongoDB pre-installed. We will do that by
14
+constructing a ``Dockerfile`` that downloads a base image, adds an
15 15
 apt source and installs the database software on Ubuntu.
16 16
 
17 17
 Creating a ``Dockerfile``
... ...
@@ -41,7 +41,7 @@ Since we want to be running the latest version of MongoDB we'll need to add the
41 41
     RUN echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/10gen.list
42 42
 
43 43
 Then, we don't want Ubuntu to complain about init not being available so we'll
44
-divert /sbin/initctl to /bin/true so it thinks everything is working.
44
+divert ``/sbin/initctl`` to ``/bin/true`` so it thinks everything is working.
45 45
 
46 46
 .. code-block:: bash
47 47
 
... ...
@@ -65,8 +65,8 @@ run without needing to provide a special configuration file)
65 65
     # Create the MongoDB data directory
66 66
     RUN mkdir -p /data/db
67 67
 
68
-Finally, we'll expose the standard port that MongoDB runs on (27107) as well as
69
-define an ENTRYPOINT for the container.
68
+Finally, we'll expose the standard port that MongoDB runs on, 27107, as well as
69
+define an ``ENTRYPOINT`` instruction for the container.
70 70
 
71 71
 .. code-block:: bash
72 72
 
... ...
@@ -78,7 +78,7 @@ run all of the commands.
78 78
 
79 79
 .. code-block:: bash
80 80
 
81
-    docker build -t <yourname>/mongodb .
81
+    sudo docker build -t <yourname>/mongodb .
82 82
 
83 83
 Now you should be able to run ``mongod`` as a daemon and be able to connect on
84 84
 the local port!
... ...
@@ -86,13 +86,13 @@ the local port!
86 86
 .. code-block:: bash
87 87
 
88 88
     # Regular style
89
-    MONGO_ID=$(docker run -d <yourname>/mongodb)
89
+    MONGO_ID=$(sudo docker run -d <yourname>/mongodb)
90 90
 
91 91
     # Lean and mean
92
-    MONGO_ID=$(docker run -d <yourname>/mongodb --noprealloc --smallfiles)
92
+    MONGO_ID=$(sudo docker run -d <yourname>/mongodb --noprealloc --smallfiles)
93 93
 
94 94
     # Check the logs out
95
-    docker logs $MONGO_ID
95
+    sudo docker logs $MONGO_ID
96 96
 
97 97
     # Connect and play around
98 98
     mongo --port <port you get from `docker ps`>
... ...
@@ -10,7 +10,7 @@ Node.js Web App
10 10
 .. include:: example_header.inc
11 11
 
12 12
 The goal of this example is to show you how you can build your own
13
-docker images from a parent image using a ``Dockerfile`` . We will do
13
+Docker images from a parent image using a ``Dockerfile`` . We will do
14 14
 that by making a simple Node.js hello world web application running on
15 15
 CentOS. You can get the full source code at
16 16
 https://github.com/gasi/docker-node-hello.
... ...
@@ -55,7 +55,7 @@ Then, create an ``index.js`` file that defines a web app using the
55 55
 
56 56
 
57 57
 In the next steps, we’ll look at how you can run this app inside a CentOS
58
-container using docker. First, you’ll need to build a docker image of your app.
58
+container using Docker. First, you’ll need to build a Docker image of your app.
59 59
 
60 60
 Creating a ``Dockerfile``
61 61
 +++++++++++++++++++++++++
... ...
@@ -67,8 +67,8 @@ Create an empty file called ``Dockerfile``:
67 67
     touch Dockerfile
68 68
 
69 69
 Open the ``Dockerfile`` in your favorite text editor and add the following line
70
-that defines the version of docker the image requires to build
71
-(this example uses docker 0.3.4):
70
+that defines the version of Docker the image requires to build
71
+(this example uses Docker 0.3.4):
72 72
 
73 73
 .. code-block:: bash
74 74
 
... ...
@@ -76,7 +76,7 @@ that defines the version of docker the image requires to build
76 76
 
77 77
 Next, define the parent image you want to use to build your own image on top of.
78 78
 Here, we’ll use `CentOS <https://index.docker.io/_/centos/>`_ (tag: ``6.4``)
79
-available on the `docker index`_:
79
+available on the `Docker index`_:
80 80
 
81 81
 .. code-block:: bash
82 82
 
... ...
@@ -95,23 +95,23 @@ To install the right package for CentOS, we’ll use the instructions from the
95 95
     # Install Node.js and npm
96 96
     RUN     yum install -y npm
97 97
 
98
-To bundle your app’s source code inside the docker image, use the ``ADD``
99
-command:
98
+To bundle your app’s source code inside the Docker image, use the ``ADD``
99
+instruction:
100 100
 
101 101
 .. code-block:: bash
102 102
 
103 103
     # Bundle app source
104 104
     ADD . /src
105 105
 
106
-Install your app dependencies using npm:
106
+Install your app dependencies using the ``npm`` binary:
107 107
 
108 108
 .. code-block:: bash
109 109
 
110 110
     # Install app dependencies
111 111
     RUN cd /src; npm install
112 112
 
113
-Your app binds to port ``8080`` so you’ll use the ``EXPOSE`` command
114
-to have it mapped by the docker daemon:
113
+Your app binds to port ``8080`` so you’ll use the ``EXPOSE`` instruction
114
+to have it mapped by the ``docker`` daemon:
115 115
 
116 116
 .. code-block:: bash
117 117
 
... ...
@@ -152,7 +152,7 @@ Building your image
152 152
 +++++++++++++++++++
153 153
 
154 154
 Go to the directory that has your ``Dockerfile`` and run the following
155
-command to build a docker image. The ``-t`` flag let’s you tag your
155
+command to build a Docker image. The ``-t`` flag let’s you tag your
156 156
 image so it’s easier to find later using the ``docker images``
157 157
 command:
158 158
 
... ...
@@ -160,7 +160,7 @@ command:
160 160
 
161 161
     sudo docker build -t <your username>/centos-node-hello .
162 162
 
163
-Your image will now be listed by docker:
163
+Your image will now be listed by Docker:
164 164
 
165 165
 .. code-block:: bash
166 166
 
... ...
@@ -199,17 +199,17 @@ Print the output of your app:
199 199
 Test
200 200
 ++++
201 201
 
202
-To test your app, get the the port of your app that docker mapped:
202
+To test your app, get the the port of your app that Docker mapped:
203 203
 
204 204
 .. code-block:: bash
205 205
 
206
-    docker ps
206
+    sudo docker ps
207 207
 
208 208
     > # Example
209 209
     > ID            IMAGE                          COMMAND              ...   PORTS
210 210
     > ecce33b30ebf  gasi/centos-node-hello:latest  node /src/index.js         49160->8080
211 211
 
212
-In the example above, docker mapped the ``8080`` port of the container to
212
+In the example above, Docker mapped the ``8080`` port of the container to
213 213
 ``49160``.
214 214
 
215 215
 Now you can call your app using ``curl`` (install if needed via:
... ...
@@ -229,7 +229,7 @@ Now you can call your app using ``curl`` (install if needed via:
229 229
     > Hello World
230 230
 
231 231
 We hope this tutorial helped you get up and running with Node.js and
232
-CentOS on docker. You can get the full source code at
232
+CentOS on Docker. You can get the full source code at
233 233
 https://github.com/gasi/docker-node-hello.
234 234
 
235 235
 Continue to :ref:`running_redis_service`.
... ...
@@ -13,7 +13,7 @@ PostgreSQL Service
13 13
 
14 14
 .. note::
15 15
 
16
-    As of version 0.5.2, docker requires root privileges to run.
16
+    As of version 0.5.2, Docker requires root privileges to run.
17 17
     You have to either manually adjust your system configuration (permissions on
18 18
     /var/run/docker.sock or sudo config), or prefix `docker` with `sudo`. Check
19 19
     `this thread`_ for details.
... ...
@@ -24,8 +24,7 @@ PostgreSQL Service
24 24
 Installing PostgreSQL on Docker
25 25
 -------------------------------
26 26
 
27
-For clarity I won't be showing commands output.
28
-
27
+For clarity I won't be showing command output.
29 28
 
30 29
 Run an interactive shell in Docker container.
31 30
 
... ...
@@ -62,7 +61,7 @@ Finally, install PostgreSQL 9.2
62 62
 
63 63
 Now, create a PostgreSQL superuser role that can create databases and
64 64
 other roles.  Following Vagrant's convention the role will be named
65
-`docker` with `docker` password assigned to it.
65
+``docker`` with ``docker`` password assigned to it.
66 66
 
67 67
 .. code-block:: bash
68 68
 
... ...
@@ -108,7 +107,7 @@ Bash prompt; you can also locate it using ``docker ps -a``.
108 108
 
109 109
 .. code-block:: bash
110 110
 
111
-    docker commit <container_id> <your username>/postgresql
111
+    sudo docker commit <container_id> <your username>/postgresql
112 112
 
113 113
 Finally, run PostgreSQL server via ``docker``.
114 114
 
... ...
@@ -10,9 +10,9 @@ Python Web App
10 10
 .. include:: example_header.inc
11 11
 
12 12
 The goal of this example is to show you how you can author your own
13
-docker images using a parent image, making changes to it, and then
13
+Docker images using a parent image, making changes to it, and then
14 14
 saving the results as a new image. We will do that by making a simple
15
-hello flask web application image.
15
+hello Flask web application image.
16 16
 
17 17
 **Steps:**
18 18
 
... ...
@@ -20,22 +20,22 @@ hello flask web application image.
20 20
 
21 21
     sudo docker pull shykes/pybuilder
22 22
 
23
-We are downloading the "shykes/pybuilder" docker image
23
+We are downloading the ``shykes/pybuilder`` Docker image
24 24
 
25 25
 .. code-block:: bash
26 26
 
27 27
     URL=http://github.com/shykes/helloflask/archive/master.tar.gz
28 28
 
29
-We set a URL variable that points to a tarball of a simple helloflask web app
29
+We set a ``URL`` variable that points to a tarball of a simple helloflask web app
30 30
 
31 31
 .. code-block:: bash
32 32
 
33 33
     BUILD_JOB=$(sudo docker run -d -t shykes/pybuilder:latest /usr/local/bin/buildapp $URL)
34 34
 
35
-Inside of the "shykes/pybuilder" image there is a command called
36
-buildapp, we are running that command and passing the $URL variable
35
+Inside of the ``shykes/pybuilder`` image there is a command called
36
+``buildapp``, we are running that command and passing the ``$URL`` variable
37 37
 from step 2 to it, and running the whole thing inside of a new
38
-container. BUILD_JOB will be set with the new container_id.
38
+container. The ``BUILD_JOB`` environment variable will be set with the new container ID.
39 39
 
40 40
 .. code-block:: bash
41 41
 
... ...
@@ -43,13 +43,13 @@ container. BUILD_JOB will be set with the new container_id.
43 43
     [...]
44 44
 
45 45
 While this container is running, we can attach to the new container to
46
-see what is going on. Ctrl-C to disconnect.
46
+see what is going on. You can use Ctrl-C to disconnect.
47 47
 
48 48
 .. code-block:: bash
49 49
 
50 50
     sudo docker ps -a
51
-    
52
-List all docker containers. If this container has already finished
51
+
52
+List all Docker containers. If this container has already finished
53 53
 running, it will still be listed here.
54 54
 
55 55
 .. code-block:: bash
... ...
@@ -57,8 +57,8 @@ running, it will still be listed here.
57 57
     BUILD_IMG=$(sudo docker commit $BUILD_JOB _/builds/github.com/shykes/helloflask/master)
58 58
 
59 59
 Save the changes we just made in the container to a new image called
60
-``_/builds/github.com/hykes/helloflask/master`` and save the image id in
61
-the BUILD_IMG variable name.
60
+``_/builds/github.com/hykes/helloflask/master`` and save the image ID in
61
+the ``BUILD_IMG`` variable name.
62 62
 
63 63
 .. code-block:: bash
64 64
 
... ...
@@ -72,24 +72,24 @@ the BUILD_IMG variable name.
72 72
 - **/usr/local/bin/runapp** is the command which starts the web app.
73 73
 
74 74
 Use the new image we just created and create a new container with
75
-network port 5000, and return the container id and store in the
76
-WEB_WORKER variable.
75
+network port 5000, and return the container ID and store in the
76
+``WEB_WORKER`` variable.
77 77
 
78 78
 .. code-block:: bash
79 79
 
80 80
     sudo docker logs $WEB_WORKER
81 81
      * Running on http://0.0.0.0:5000/
82 82
 
83
-View the logs for the new container using the WEB_WORKER variable, and
84
-if everything worked as planned you should see the line "Running on
85
-http://0.0.0.0:5000/" in the log output.
83
+View the logs for the new container using the ``WEB_WORKER`` variable, and
84
+if everything worked as planned you should see the line ``Running on
85
+http://0.0.0.0:5000/`` in the log output.
86 86
 
87 87
 .. code-block:: bash
88 88
 
89 89
     WEB_PORT=$(sudo docker port $WEB_WORKER 5000)
90 90
 
91 91
 Look up the public-facing port which is NAT-ed. Find the private port
92
-used by the container and store it inside of the WEB_PORT variable.
92
+used by the container and store it inside of the ``WEB_PORT`` variable.
93 93
 
94 94
 .. code-block:: bash
95 95
 
... ...
@@ -97,8 +97,8 @@ used by the container and store it inside of the WEB_PORT variable.
97 97
     curl http://127.0.0.1:$WEB_PORT
98 98
       Hello world!
99 99
 
100
-Access the web app using curl. If everything worked as planned you
101
-should see the line "Hello world!" inside of your console.
100
+Access the web app using the ``curl`` binary. If everything worked as planned you
101
+should see the line ``Hello world!`` inside of your console.
102 102
 
103 103
 **Video:**
104 104
 
... ...
@@ -9,7 +9,7 @@ Redis Service
9 9
 
10 10
 .. include:: example_header.inc
11 11
 
12
-Very simple, no frills, redis service.
12
+Very simple, no frills, Redis service.
13 13
 
14 14
 Open a docker container
15 15
 -----------------------
... ...
@@ -35,13 +35,13 @@ Snapshot the installation
35 35
 
36 36
 .. code-block:: bash
37 37
 
38
-    docker ps -a  # grab the container id (this will be the first one in the list)
39
-    docker commit <container_id> <your username>/redis
38
+    sudo docker ps -a  # grab the container id (this will be the first one in the list)
39
+    sudo docker commit <container_id> <your username>/redis
40 40
 
41 41
 Run the service
42 42
 ---------------
43 43
 
44
-Running the service with `-d` runs the container in detached mode, leaving the
44
+Running the service with ``-d`` runs the container in detached mode, leaving the
45 45
 container running in the background. Use your snapshot.
46 46
 
47 47
 .. code-block:: bash
... ...
@@ -51,7 +51,7 @@ container running in the background. Use your snapshot.
51 51
 Test 1
52 52
 ++++++
53 53
 
54
-Connect to the container with the redis-cli.
54
+Connect to the container with the ``redis-cli`` binary.
55 55
 
56 56
 .. code-block:: bash
57 57
 
... ...
@@ -67,7 +67,7 @@ Connect to the container with the redis-cli.
67 67
 Test 2
68 68
 ++++++
69 69
 
70
-Connect to the host os with the redis-cli.
70
+Connect to the host os with the ``redis-cli`` binary.
71 71
 
72 72
 .. code-block:: bash
73 73
 
... ...
@@ -107,7 +107,7 @@ Create a ``supervisord`` configuration file
107 107
 +++++++++++++++++++++++++++++++++++++++++++
108 108
 
109 109
 Create an empty file called ``supervisord.conf``. Make sure it's at the same
110
-level as your ``Dockerfile``:
110
+directory level as your ``Dockerfile``:
111 111
 
112 112
 .. code-block:: bash
113 113
 
... ...
@@ -12,14 +12,14 @@ SSH Daemon Service
12 12
 
13 13
 **Video:**
14 14
 
15
-I've create a little screencast to show how to create a sshd service
15
+I've create a little screencast to show how to create a SSHd service
16 16
 and connect to it. It is something like 11 minutes and not entirely
17 17
 smooth, but gives you a good idea.
18 18
 
19 19
 .. note::
20
-   This screencast was created before ``docker`` version 0.5.2, so the
20
+   This screencast was created before Docker version 0.5.2, so the
21 21
    daemon is unprotected and available via a TCP port. When you run
22
-   through the same steps in a newer version of ``docker``, you will
22
+   through the same steps in a newer version of Docker, you will
23 23
    need to add ``sudo`` in front of each ``docker`` command in order
24 24
    to reach the daemon over its protected Unix socket.
25 25
 
... ...
@@ -29,13 +29,14 @@ smooth, but gives you a good idea.
29 29
       <iframe width="800" height="400" src="http://ascii.io/a/2637/raw" frameborder="0"></iframe>
30 30
     </div>
31 31
         
32
-You can also get this sshd container by using
33
-::
32
+You can also get this sshd container by using:
33
+
34
+.. code-block:: bash
34 35
 
35 36
     sudo docker pull dhrp/sshd
36 37
 
37 38
 
38
-The password is 'screencast'
39
+The password is ``screencast``.
39 40
 
40 41
 **Video's Transcription:**
41 42
 
... ...
@@ -162,8 +162,8 @@ Verify it worked
162 162
 Docker and UFW
163 163
 ^^^^^^^^^^^^^^
164 164
 
165
-Docker uses a bridge to manage containers networking, by default UFW
166
-drop all `forwarding`, a first step is to enable forwarding:
165
+Docker uses a bridge to manage container networking. By default, UFW
166
+drops all `forwarding`, thus a first step is to enable UFW forwarding:
167 167
 
168 168
 .. code-block:: bash
169 169
 
... ...
@@ -9,7 +9,7 @@ Using Vagrant (Mac, Linux)
9 9
 
10 10
 This guide will setup a new virtualbox virtual machine with docker
11 11
 installed on your computer. This works on most operating systems,
12
-including MacOX, Windows, Linux, FreeBSD and others. If you can
12
+including MacOSX, Windows, Linux, FreeBSD and others. If you can
13 13
 install these and have at least 400MB RAM to spare you should be good.
14 14
 
15 15
 Install Vagrant and Virtualbox
... ...
@@ -208,6 +208,10 @@ configuration / Device configuration)
208 208
 
209 209
 .. image:: images/win/hp_bios_vm.JPG
210 210
 
211
+On some machines the BIOS menu can only be accessed before startup.
212
+To access BIOS in this scenario you should restart your computer and 
213
+press ESC/Enter when prompted to access the boot and BIOS controls. Typically
214
+the option to allow virtualization is contained within the BIOS/Security menu.
211 215
 
212 216
 Docker is not installed
213 217
 ```````````````````````
... ...
@@ -138,22 +138,19 @@ Listing all running containers
138 138
 
139 139
   sudo docker ps
140 140
 
141
-Expose a service on a TCP port
141
+Bind a service on a TCP port
142 142
 ------------------------------
143 143
 
144 144
 .. code-block:: bash
145 145
 
146
-  # Expose port 4444 of this container, and tell netcat to listen on it
146
+  # Bind port 4444 of this container, and tell netcat to listen on it
147 147
   JOB=$(sudo docker run -d -p 4444 ubuntu:12.10 /bin/nc -l 4444)
148 148
 
149 149
   # Which public port is NATed to my container?
150
-  PORT=$(sudo docker port $JOB 4444)
150
+  PORT=$(sudo docker port $JOB 4444 | awk -F: '{ print $2 }')
151 151
 
152
-  # Connect to the public port via the host's public address
153
-  # Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work.
154
-  # Replace *eth0* according to your local interface name.
155
-  IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }')
156
-  echo hello world | nc $IP $PORT
152
+  # Connect to the public port
153
+  echo hello world | nc 127.0.0.1 $PORT
157 154
 
158 155
   # Verify that the network connection worked
159 156
   echo "Daemon received: $(sudo docker logs $JOB)"
... ...
@@ -183,4 +180,3 @@ You now have a image state from which you can create new instances.
183 183
 
184 184
 Read more about :ref:`working_with_the_repository` or continue to the
185 185
 complete :ref:`cli`
186
-
... ...
@@ -174,10 +174,10 @@ override the default specified in CMD.
174 174
 
175 175
     ``EXPOSE <port> [<port>...]``
176 176
 
177
-The ``EXPOSE`` instruction sets ports to be publicly exposed when
178
-running the image. This is functionally equivalent to running ``docker
179
-commit -run '{"PortSpecs": ["<port>", "<port2>"]}'`` outside the
180
-builder. Take a look at :ref:`port_redirection` for more information.
177
+The ``EXPOSE`` instruction exposes ports for use within links. This is
178
+functionally equivalent to running ``docker commit -run '{"PortSpecs":
179
+["<port>", "<port2>"]}'`` outside the builder. Refer to
180
+:ref:`port_redirection` for detailed information.
181 181
 
182 182
 3.6 ENV
183 183
 -------
... ...
@@ -208,6 +208,9 @@ a remote file URL.
208 208
 ``<dest>`` is the path at which the source will be copied in the
209 209
 destination container.
210 210
 
211
+All new files and directories are created with mode 0755, uid and gid
212
+0.
213
+
211 214
 The copy obeys the following rules:
212 215
 
213 216
 * If ``<src>`` is a URL and ``<dest>`` does not end with a trailing slash,
... ...
@@ -220,8 +223,9 @@ The copy obeys the following rules:
220 220
   (``http://example.com`` will not work).
221 221
 * If ``<src>`` is a directory, the entire directory is copied,
222 222
   including filesystem metadata.
223
-* If ``<src>`` is a tar archive in a recognized compression format
224
-  (identity, gzip, bzip2 or xz), it is unpacked as a directory.
223
+* If ``<src>`` is a *local* tar archive in a recognized compression
224
+  format (identity, gzip, bzip2 or xz) then it is unpacked as a
225
+  directory. Resources from *remote* URLs are **not** decompressed.
225 226
 
226 227
   When a directory is copied or unpacked, it has the same behavior as
227 228
   ``tar -x``: the result is the union of
... ...
@@ -229,7 +233,7 @@ The copy obeys the following rules:
229 229
   1. whatever existed at the destination path and
230 230
   2. the contents of the source tree,
231 231
 
232
-  with conflicts resolved in favor of 2) on a file-by-file basis.
232
+  with conflicts resolved in favor of "2." on a file-by-file basis.
233 233
 
234 234
 * If ``<src>`` is any other kind of file, it is copied individually
235 235
   along with its metadata. In this case, if ``<dest>`` ends with a
... ...
@@ -237,10 +241,9 @@ The copy obeys the following rules:
237 237
   contents of ``<src>`` will be written at ``<dest>/base(<src>)``.
238 238
 * If ``<dest>`` does not end with a trailing slash, it will be
239 239
   considered a regular file and the contents of ``<src>`` will be
240
-  written at ``<dst>``.
240
+  written at ``<dest>``.
241 241
 * If ``<dest>`` doesn't exist, it is created along with all missing
242
-  directories in its path. All new files and directories are created
243
-  with mode 0755, uid and gid 0.
242
+  directories in its path.
244 243
 
245 244
 .. _entrypoint_def:
246 245
 
... ...
@@ -19,3 +19,4 @@ Contents:
19 19
    port_redirection
20 20
    puppet
21 21
    host_integration
22
+   working_with_volumes
... ...
@@ -8,29 +8,136 @@
8 8
 Port redirection
9 9
 ================
10 10
 
11
-Docker can redirect public TCP and UDP ports to your container, so it can be
12
-reached over the network.  Port redirection is done on ``docker run``
13
-using the -p flag.
11
+Interacting with a service is commonly done through a connection to a
12
+port. When this service runs inside a container, one can connect to
13
+the port after finding the IP address of the container as follows:
14 14
 
15
-A port redirect is specified as *PUBLIC:PRIVATE*, where TCP port
16
-*PUBLIC* will be redirected to TCP port *PRIVATE*. As a special case,
17
-the public port can be omitted, in which case a random public port
18
-will be allocated.
15
+.. code-block:: bash
16
+
17
+    # Find IP address of container with ID <container_id>
18
+    docker inspect <container_id> | grep IPAddress | cut -d '"' -f 4
19
+
20
+However, this IP address is local to the host system and the container
21
+port is not reachable by the outside world. Furthermore, even if the
22
+port is used locally, e.g. by another container, this method is
23
+tedious as the IP address of the container changes every time it
24
+starts.
25
+
26
+Docker addresses these two problems and give a simple and robust way
27
+to access services running inside containers.
28
+
29
+To allow non-local clients to reach the service running inside the
30
+container, Docker provide ways to bind the container port to an
31
+interface of the host system. To simplify communication between
32
+containers, Docker provides the linking mechanism.
33
+
34
+Binding a port to an host interface
35
+-----------------------------------
36
+
37
+To bind a port of the container to a specific interface of the host
38
+system, use the ``-p`` parameter of the ``docker run`` command:
19 39
 
20 40
 .. code-block:: bash
21 41
 
22
-    # A random PUBLIC port is redirected to PRIVATE port 80 on the container
23
-    sudo docker run -p 80 <image> <cmd>
42
+    # General syntax
43
+    docker run -p [([<host_interface>:[host_port]])|(<host_port>):]<container_port>[/udp] <image> <cmd>
44
+
45
+When no host interface is provided, the port is bound to all available
46
+interfaces of the host machine (aka INADDR_ANY, or 0.0.0.0).When no host port is
47
+provided, one is dynamically allocated. The possible combinations of options for
48
+TCP port are the following:
49
+
50
+.. code-block:: bash
24 51
 
25
-    # PUBLIC port 80 is redirected to PRIVATE port 80
26
-    sudo docker run -p 80:80 <image> <cmd>
52
+    # Bind TCP port 8080 of the container to TCP port 80 on 127.0.0.1 of the host machine.
53
+    docker run -p 127.0.0.1:80:8080 <image> <cmd>
27 54
 
28
-To redirect a UDP port the redirection must be expressed as *PUBLIC:PRIVATE/udp*:
55
+    # Bind TCP port 8080 of the container to a dynamically allocated TCP port on 127.0.0.1 of the host machine.
56
+    docker run -p 127.0.0.1::8080 <image> <cmd>
57
+
58
+    # Bind TCP port 8080 of the container to TCP port 80 on all available interfaces of the host machine.
59
+    docker run -p 80:8080 <image> <cmd>
60
+
61
+    # Bind TCP port 8080 of the container to a dynamically allocated TCP port on all available interfaces of the host machine.
62
+    docker run -p 8080 <image> <cmd>
63
+
64
+UDP ports can also be bound by adding a trailing ``/udp``. All the
65
+combinations described for TCP work. Here is only one example:
29 66
 
30 67
 .. code-block:: bash
31 68
 
32
-    # PUBLIC port 5300 is redirected to the PRIVATE port 53 using UDP
33
-    sudo docker run -p 5300:53/udp <image> <cmd>
69
+    # Bind UDP port 5353 of the container to UDP port 53 on 127.0.0.1 of the host machine.
70
+    docker run -p 127.0.0.1:53:5353/udp <image> <cmd>
71
+
72
+The command ``docker port`` lists the interface and port on the host
73
+machine bound to a given container port. It is useful when using
74
+dynamically allocated ports:
75
+
76
+.. code-block:: bash
77
+
78
+   # Bind to a dynamically allocated port
79
+   docker run -p 127.0.0.1::8080 -name dyn-bound <image> <cmd>
80
+
81
+   # Lookup the actual port
82
+   docker port dyn-bound 8080
83
+   127.0.0.1:49160
84
+
85
+
86
+Linking a container
87
+-------------------
88
+
89
+Communication between two containers can also be established in a
90
+docker-specific way called linking.
91
+
92
+To briefly present the concept of linking, let us consider two
93
+containers: ``server``, containing the service, and ``client``,
94
+accessing the service.  Once ``server`` is running, ``client`` is
95
+started and links to server. Linking sets environment variables in
96
+``client`` giving it some information about ``server``. In this sense,
97
+linking is a method of service discovery.
98
+
99
+Let us now get back to our topic of interest; communication between
100
+the two containers. We mentioned that the tricky part about this
101
+communication was that the IP address of ``server`` was not
102
+fixed. Therefore, some of the environment variables are going to be
103
+used to inform ``client`` about this IP address. This process called
104
+exposure, is possible because ``client`` is started after ``server``
105
+has been started.
106
+
107
+Here is a full example. On ``server``, the port of interest is
108
+exposed. The exposure is done either through the ``-expose`` parameter
109
+to the ``docker run`` command, or the ``EXPOSE`` build command in a
110
+Dockerfile:
111
+
112
+.. code-block:: bash
113
+
114
+    # Expose port 80
115
+    docker run -expose 80 -name server <image> <cmd>
116
+
117
+The ``client`` then links to the ``server``:
118
+
119
+.. code-block:: bash
120
+
121
+    # Link
122
+    docker run -name client -link server:linked-server <image> <cmd>
123
+
124
+``client`` locally refers to ``server`` as ``linked-server``. The
125
+following environment variables, among others, are available on
126
+``client``:
127
+
128
+.. code-block:: bash
129
+
130
+    # The default protocol, ip, and port of the service running in the container
131
+    LINKED-SERVER_PORT=tcp://172.17.0.8:80
132
+
133
+    # A specific protocol, ip, and port of various services
134
+    LINKED-SERVER_PORT_80_TCP=tcp://172.17.0.8:80
135
+    LINKED-SERVER_PORT_80_TCP_PROTO=tcp
136
+    LINKED-SERVER_PORT_80_TCP_ADDR=172.17.0.8
137
+    LINKED-SERVER_PORT_80_TCP_PORT=80
138
+
139
+This tells ``client`` that a service is running on port 80 of
140
+``server`` and that ``server`` is accessible at the IP address
141
+172.17.0.8
34 142
 
35
-Default port redirects can be built into a container with the
36
-``EXPOSE`` build command.
143
+Note: Using the ``-p`` parameter also exposes the port..
37 144
new file mode 100644
... ...
@@ -0,0 +1,73 @@
0
+:title: Working with Volumes
1
+:description: How to create and share volumes
2
+:keywords: Examples, Usage, volume, docker, documentation, examples
3
+
4
+.. _volume_def:
5
+
6
+Data Volume
7
+===========
8
+
9
+.. versionadded:: v0.3.0
10
+   Data volumes have been available since version 1 of the
11
+   :doc:`../api/docker_remote_api`
12
+
13
+A *data volume* is a specially-designated directory within one or more
14
+containers that bypasses the :ref:`ufs_def` to provide several useful
15
+features for persistant or shared data:
16
+
17
+* **Data volumes can be shared and reused between containers.** This
18
+  is the feature that makes data volumes so powerful. You can use it
19
+  for anything from hot database upgrades to custom backup or
20
+  replication tools. See the example below.
21
+* **Changes to a data volume are made directly**, without the overhead
22
+  of a copy-on-write mechanism. This is good for very large files.
23
+* **Changes to a data volume will not be included at the next commit**
24
+  because they are not recorded as regular filesystem changes in the
25
+  top layer of the :ref:`ufs_def`
26
+
27
+Each container can have zero or more data volumes.
28
+
29
+Getting Started
30
+...............
31
+
32
+
33
+
34
+Using data volumes is as simple as adding a new flag: ``-v``. The parameter ``-v`` can be used more than once in order to create more volumes within the new container. The example below shows the instruction to create a container with two new volumes::
35
+
36
+  docker run -v /var/volume1 -v /var/volume2 shykes/couchdb
37
+
38
+For a Dockerfile, the VOLUME instruction will add one or more new volumes to any container created from the image::
39
+
40
+  VOLUME ["/var/volume1", "/var/volume2"]
41
+
42
+
43
+Create a new container using existing volumes from an existing container:
44
+---------------------------------------------------------------------------
45
+
46
+
47
+The command below creates a new container which is runnning as daemon ``-d`` and with one volume ``/var/lib/couchdb``::
48
+
49
+  COUCH1=$(sudo docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03)
50
+
51
+From the container id of that previous container ``$COUCH1`` it's possible to create new container sharing the same volume using the parameter ``-volumes-from container_id``::
52
+
53
+  COUCH2=$(sudo docker run -d -volumes-from $COUCH1 shykes/couchdb:2013-05-03)
54
+
55
+Now, the second container has the all the information from the first volume.
56
+
57
+
58
+Create a new container which mounts a host directory into it:
59
+-------------------------------------------------------------
60
+
61
+  -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro].
62
+  If "host-dir" is missing, then docker creates a new volume.
63
+
64
+  This is not available for a Dockerfile due the portability and sharing purpose of it. The [host-dir] volumes is something    100% host dependent and will break on any other machine.
65
+
66
+For example::
67
+
68
+  sudo docker run -v /var/logs:/var/host_logs:ro shykes/couchdb:2013-05-03
69
+
70
+The command above mounts the host directory ``/var/logs`` into the container with read only permissions as ``/var/host_logs``.
71
+
72
+.. versionadded:: v0.5.0
0 73
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Solomon Hykes <solomon@dotcloud.com>
0 1
new file mode 100644
... ...
@@ -0,0 +1,82 @@
0
+package engine
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+	"log"
6
+	"runtime"
7
+	"github.com/dotcloud/docker/utils"
8
+)
9
+
10
+
11
+type Handler func(*Job) string
12
+
13
+var globalHandlers map[string]Handler
14
+
15
+func Register(name string, handler Handler) error {
16
+	if globalHandlers == nil {
17
+		globalHandlers = make(map[string]Handler)
18
+	}
19
+	globalHandlers[name] = handler
20
+	return nil
21
+}
22
+
23
+// The Engine is the core of Docker.
24
+// It acts as a store for *containers*, and allows manipulation of these
25
+// containers by executing *jobs*.
26
+type Engine struct {
27
+	root		string
28
+	handlers	map[string]Handler
29
+}
30
+
31
+// New initializes a new engine managing the directory specified at `root`.
32
+// `root` is used to store containers and any other state private to the engine.
33
+// Changing the contents of the root without executing a job will cause unspecified
34
+// behavior.
35
+func New(root string) (*Engine, error) {
36
+	// Check for unsupported architectures
37
+	if runtime.GOARCH != "amd64" {
38
+		return nil, fmt.Errorf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
39
+	}
40
+	// Check for unsupported kernel versions
41
+	// FIXME: it would be cleaner to not test for specific versions, but rather
42
+	// test for specific functionalities.
43
+	// Unfortunately we can't test for the feature "does not cause a kernel panic"
44
+	// without actually causing a kernel panic, so we need this workaround until
45
+	// the circumstances of pre-3.8 crashes are clearer.
46
+	// For details see http://github.com/dotcloud/docker/issues/407
47
+	if k, err := utils.GetKernelVersion(); err != nil {
48
+		log.Printf("WARNING: %s\n", err)
49
+	} else {
50
+		if utils.CompareKernelVersion(k, &utils.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 {
51
+			log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
52
+		}
53
+	}
54
+	if err := os.MkdirAll(root, 0700); err != nil && !os.IsExist(err) {
55
+		return nil, err
56
+	}
57
+	eng := &Engine{
58
+		root:		root,
59
+		handlers:	globalHandlers,
60
+	}
61
+	return eng, nil
62
+}
63
+
64
+// Job creates a new job which can later be executed.
65
+// This function mimics `Command` from the standard os/exec package.
66
+func (eng *Engine) Job(name string, args ...string) *Job {
67
+	job := &Job{
68
+		eng:		eng,
69
+		Name:		name,
70
+		Args:		args,
71
+		Stdin:		os.Stdin,
72
+		Stdout:		os.Stdout,
73
+		Stderr:		os.Stderr,
74
+	}
75
+	handler, exists := eng.handlers[name]
76
+	if exists {
77
+		job.handler = handler
78
+	}
79
+	return job
80
+}
81
+
0 82
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+package engine
1
+
2
+import (
3
+	"testing"
4
+)
5
+
6
+func TestNewJob(t *testing.T) {
7
+	job := mkJob(t, "dummy", "--level=awesome")
8
+	if job.Name != "dummy" {
9
+		t.Fatalf("Wrong job name: %s", job.Name)
10
+	}
11
+	if len(job.Args) != 1 {
12
+		t.Fatalf("Wrong number of job arguments: %d", len(job.Args))
13
+	}
14
+	if job.Args[0] != "--level=awesome" {
15
+		t.Fatalf("Wrong job arguments: %s", job.Args[0])
16
+	}
17
+}
18
+
19
+func TestSetenv(t *testing.T) {
20
+	job := mkJob(t, "dummy")
21
+	job.Setenv("foo", "bar")
22
+	if val := job.Getenv("foo"); val != "bar" {
23
+		t.Fatalf("Getenv returns incorrect value: %s", val)
24
+	}
25
+	if val := job.Getenv("nonexistent"); val != "" {
26
+		t.Fatalf("Getenv returns incorrect value: %s", val)
27
+	}
28
+}
0 29
new file mode 100644
... ...
@@ -0,0 +1,42 @@
0
+package engine
1
+
2
+import (
3
+	"testing"
4
+	"runtime"
5
+	"strings"
6
+	"fmt"
7
+	"io/ioutil"
8
+	"github.com/dotcloud/docker/utils"
9
+)
10
+
11
+var globalTestID string
12
+
13
+func init() {
14
+	Register("dummy", func(job *Job) string { return ""; })
15
+}
16
+
17
+func mkEngine(t *testing.T) *Engine {
18
+	// Use the caller function name as a prefix.
19
+	// This helps trace temp directories back to their test.
20
+	pc, _, _, _ := runtime.Caller(1)
21
+	callerLongName := runtime.FuncForPC(pc).Name()
22
+	parts := strings.Split(callerLongName, ".")
23
+	callerShortName := parts[len(parts)-1]
24
+	if globalTestID == "" {
25
+		globalTestID = utils.RandomString()[:4]
26
+	}
27
+	prefix := fmt.Sprintf("docker-test%s-%s-", globalTestID, callerShortName)
28
+	root, err := ioutil.TempDir("", prefix)
29
+	if err != nil {
30
+		t.Fatal(err)
31
+	}
32
+	eng, err := New(root)
33
+	if err != nil {
34
+		t.Fatal(err)
35
+	}
36
+	return eng
37
+}
38
+
39
+func mkJob(t *testing.T, name string, args ...string) *Job {
40
+	return mkEngine(t).Job(name, args...)
41
+}
0 42
new file mode 100644
... ...
@@ -0,0 +1,113 @@
0
+package engine
1
+
2
+import (
3
+	"io"
4
+	"strings"
5
+	"fmt"
6
+	"encoding/json"
7
+	"github.com/dotcloud/docker/utils"
8
+)
9
+
10
+// A job is the fundamental unit of work in the docker engine.
11
+// Everything docker can do should eventually be exposed as a job.
12
+// For example: execute a process in a container, create a new container,
13
+// download an archive from the internet, serve the http api, etc.
14
+//
15
+// The job API is designed after unix processes: a job has a name, arguments,
16
+// environment variables, standard streams for input, output and error, and
17
+// an exit status which can indicate success (0) or error (anything else).
18
+//
19
+// One slight variation is that jobs report their status as a string. The
20
+// string "0" indicates success, and any other strings indicates an error.
21
+// This allows for richer error reporting.
22
+// 
23
+type Job struct {
24
+	eng	*Engine
25
+	Name	string
26
+	Args	[]string
27
+	env	[]string
28
+	Stdin	io.ReadCloser
29
+	Stdout	io.WriteCloser
30
+	Stderr	io.WriteCloser
31
+	handler	func(*Job) string
32
+	status	string
33
+}
34
+
35
+// Run executes the job and blocks until the job completes.
36
+// If the job returns a failure status, an error is returned
37
+// which includes the status.
38
+func (job *Job) Run() error {
39
+	randId := utils.RandomString()[:4]
40
+	fmt.Printf("Job #%s: %s\n", randId, job)
41
+	defer fmt.Printf("Job #%s: %s = '%s'", randId, job, job.status)
42
+	if job.handler == nil {
43
+		job.status = "command not found"
44
+	} else {
45
+		job.status = job.handler(job)
46
+	}
47
+	if job.status != "0" {
48
+		return fmt.Errorf("%s: %s", job.Name, job.status)
49
+	}
50
+	return nil
51
+}
52
+
53
+// String returns a human-readable description of `job`
54
+func (job *Job) String() string {
55
+	return strings.Join(append([]string{job.Name}, job.Args...), " ")
56
+}
57
+
58
+func (job *Job) Getenv(key string) (value string) {
59
+        for _, kv := range job.env {
60
+                if strings.Index(kv, "=") == -1 {
61
+                        continue
62
+                }
63
+                parts := strings.SplitN(kv, "=", 2)
64
+                if parts[0] != key {
65
+                        continue
66
+                }
67
+                if len(parts) < 2 {
68
+                        value = ""
69
+                } else {
70
+                        value = parts[1]
71
+                }
72
+        }
73
+        return
74
+}
75
+
76
+func (job *Job) GetenvBool(key string) (value bool) {
77
+	s := strings.ToLower(strings.Trim(job.Getenv(key), " \t"))
78
+	if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
79
+		return false
80
+	}
81
+	return true
82
+}
83
+
84
+func (job *Job) SetenvBool(key string, value bool) {
85
+	if value {
86
+		job.Setenv(key, "1")
87
+	} else {
88
+		job.Setenv(key, "0")
89
+	}
90
+}
91
+
92
+func (job *Job) GetenvList(key string) []string {
93
+	sval := job.Getenv(key)
94
+	l := make([]string, 0, 1)
95
+	if err := json.Unmarshal([]byte(sval), &l); err != nil {
96
+		l = append(l, sval)
97
+	}
98
+	return l
99
+}
100
+
101
+func (job *Job) SetenvList(key string, value []string) error {
102
+	sval, err := json.Marshal(value)
103
+	if err != nil {
104
+		return err
105
+	}
106
+	job.Setenv(key, string(sval))
107
+	return nil
108
+}
109
+
110
+func (job *Job) Setenv(key, value string) {
111
+	job.env = append(job.env, key + "=" + value)
112
+}
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"database/sql"
5 5
 	"fmt"
6 6
 	"path"
7
+	"sync"
7 8
 )
8 9
 
9 10
 const (
... ...
@@ -47,6 +48,7 @@ type WalkFunc func(fullPath string, entity *Entity) error
47 47
 // Graph database for storing entities and their relationships
48 48
 type Database struct {
49 49
 	conn *sql.DB
50
+	mux  sync.Mutex
50 51
 }
51 52
 
52 53
 // Create a new graph database initialized with a root entity
... ...
@@ -54,7 +56,7 @@ func NewDatabase(conn *sql.DB, init bool) (*Database, error) {
54 54
 	if conn == nil {
55 55
 		return nil, fmt.Errorf("Database connection cannot be nil")
56 56
 	}
57
-	db := &Database{conn}
57
+	db := &Database{conn: conn}
58 58
 
59 59
 	if init {
60 60
 		if _, err := conn.Exec(createEntityTable); err != nil {
... ...
@@ -99,7 +101,9 @@ func (db *Database) Close() error {
99 99
 
100 100
 // Set the entity id for a given path
101 101
 func (db *Database) Set(fullPath, id string) (*Entity, error) {
102
-	// FIXME: is rollback implicit when closing the connection?
102
+	db.mux.Lock()
103
+	defer db.mux.Unlock()
104
+
103 105
 	rollback := func() {
104 106
 		db.conn.Exec("ROLLBACK")
105 107
 	}
... ...
@@ -256,6 +260,9 @@ func (db *Database) RefPaths(id string) Edges {
256 256
 
257 257
 // Delete the reference to an entity at a given path
258 258
 func (db *Database) Delete(name string) error {
259
+	db.mux.Lock()
260
+	defer db.mux.Unlock()
261
+
259 262
 	if name == "/" {
260 263
 		return fmt.Errorf("Cannot delete root entity")
261 264
 	}
... ...
@@ -276,6 +283,9 @@ func (db *Database) Delete(name string) error {
276 276
 // Walk the graph to make sure all references to the entity
277 277
 // are removed and return the number of references removed
278 278
 func (db *Database) Purge(id string) (int, error) {
279
+	db.mux.Lock()
280
+	defer db.mux.Unlock()
281
+
279 282
 	rollback := func() {
280 283
 		db.conn.Exec("ROLLBACK")
281 284
 	}
... ...
@@ -310,6 +320,9 @@ func (db *Database) Purge(id string) (int, error) {
310 310
 
311 311
 // Rename an edge for a given path
312 312
 func (db *Database) Rename(currentName, newName string) error {
313
+	db.mux.Lock()
314
+	defer db.mux.Unlock()
315
+
313 316
 	parentPath, name := splitPath(currentName)
314 317
 	newParentPath, newEdgeName := splitPath(newName)
315 318
 
... ...
@@ -3,6 +3,7 @@ package gograph
3 3
 import (
4 4
 	_ "code.google.com/p/gosqlite/sqlite3"
5 5
 	"database/sql"
6
+	"fmt"
6 7
 	"os"
7 8
 	"path"
8 9
 	"strconv"
... ...
@@ -501,3 +502,39 @@ func TestGetNameWithTrailingSlash(t *testing.T) {
501 501
 		t.Fatalf("Entity should not be nil")
502 502
 	}
503 503
 }
504
+
505
+func TestConcurrentWrites(t *testing.T) {
506
+	db, dbpath := newTestDb(t)
507
+	defer destroyTestDb(dbpath)
508
+
509
+	errs := make(chan error, 2)
510
+
511
+	save := func(name string, id string) {
512
+		if _, err := db.Set(fmt.Sprintf("/%s", name), id); err != nil {
513
+			errs <- err
514
+		}
515
+		errs <- nil
516
+	}
517
+	purge := func(id string) {
518
+		if _, err := db.Purge(id); err != nil {
519
+			errs <- err
520
+		}
521
+		errs <- nil
522
+	}
523
+
524
+	save("/1", "1")
525
+
526
+	go purge("1")
527
+	go save("/2", "2")
528
+
529
+	any := false
530
+	for i := 0; i < 2; i++ {
531
+		if err := <-errs; err != nil {
532
+			any = true
533
+			t.Log(err)
534
+		}
535
+	}
536
+	if any {
537
+		t.Fatal()
538
+	}
539
+}
... ...
@@ -2,6 +2,7 @@ package docker
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"github.com/dotcloud/docker/archive"
5 6
 	"github.com/dotcloud/docker/utils"
6 7
 	"io"
7 8
 	"io/ioutil"
... ...
@@ -94,7 +95,7 @@ func (graph *Graph) Get(name string) (*Image, error) {
94 94
 }
95 95
 
96 96
 // Create creates a new image and registers it in the graph.
97
-func (graph *Graph) Create(layerData Archive, container *Container, comment, author string, config *Config) (*Image, error) {
97
+func (graph *Graph) Create(layerData archive.Archive, container *Container, comment, author string, config *Config) (*Image, error) {
98 98
 	img := &Image{
99 99
 		ID:            GenerateID(),
100 100
 		Comment:       comment,
... ...
@@ -117,7 +118,7 @@ func (graph *Graph) Create(layerData Archive, container *Container, comment, aut
117 117
 
118 118
 // Register imports a pre-existing image into the graph.
119 119
 // FIXME: pass img as first argument
120
-func (graph *Graph) Register(jsonData []byte, layerData Archive, img *Image) error {
120
+func (graph *Graph) Register(jsonData []byte, layerData archive.Archive, img *Image) error {
121 121
 	if err := ValidateID(img.ID); err != nil {
122 122
 		return err
123 123
 	}
... ...
@@ -146,7 +147,7 @@ func (graph *Graph) Register(jsonData []byte, layerData Archive, img *Image) err
146 146
 //   The archive is stored on disk and will be automatically deleted as soon as has been read.
147 147
 //   If output is not nil, a human-readable progress bar will be written to it.
148 148
 //   FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives?
149
-func (graph *Graph) TempLayerArchive(id string, compression Compression, sf *utils.StreamFormatter, output io.Writer) (*TempArchive, error) {
149
+func (graph *Graph) TempLayerArchive(id string, compression archive.Compression, sf *utils.StreamFormatter, output io.Writer) (*archive.TempArchive, error) {
150 150
 	image, err := graph.Get(id)
151 151
 	if err != nil {
152 152
 		return nil, err
... ...
@@ -155,11 +156,11 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, sf *uti
155 155
 	if err != nil {
156 156
 		return nil, err
157 157
 	}
158
-	archive, err := image.TarLayer(compression)
158
+	a, err := image.TarLayer(compression)
159 159
 	if err != nil {
160 160
 		return nil, err
161 161
 	}
162
-	return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp.Root)
162
+	return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp.Root)
163 163
 }
164 164
 
165 165
 // Mktemp creates a temporary sub-directory inside the graph's filesystem.
... ...
@@ -201,6 +202,7 @@ func (graph *Graph) getDockerInitLayer() (string, error) {
201 201
 		"/proc":            "dir",
202 202
 		"/sys":             "dir",
203 203
 		"/.dockerinit":     "file",
204
+		"/.dockerenv":      "file",
204 205
 		"/etc/resolv.conf": "file",
205 206
 		"/etc/hosts":       "file",
206 207
 		"/etc/hostname":    "file",
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"archive/tar"
5 5
 	"bytes"
6 6
 	"errors"
7
+	"github.com/dotcloud/docker/archive"
7 8
 	"github.com/dotcloud/docker/utils"
8 9
 	"io"
9 10
 	"io/ioutil"
... ...
@@ -301,7 +302,7 @@ func tempGraph(t *testing.T) *Graph {
301 301
 	return graph
302 302
 }
303 303
 
304
-func testArchive(t *testing.T) Archive {
304
+func testArchive(t *testing.T) archive.Archive {
305 305
 	archive, err := fakeTar()
306 306
 	if err != nil {
307 307
 		t.Fatal(err)
... ...
@@ -9,7 +9,8 @@ to keep it up-to-date.
9 9
 ### 1. Pull from master and create a release branch
10 10
 
11 11
 ```bash
12
-git checkout master
12
+export VERSION=vXXX
13
+git checkout release
13 14
 git pull
14 15
 git checkout -b bump_$VERSION
15 16
 ```
... ...
@@ -55,7 +56,9 @@ EXAMPLES:
55 55
 
56 56
 ### 4. Run all tests
57 57
 
58
-FIXME
58
+```bash
59
+docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh test
60
+```
59 61
 
60 62
 ### 5. Test the docs
61 63
 
... ...
@@ -66,21 +69,17 @@ the docs are in ``docs/README.md``
66 66
 ### 6. Commit and create a pull request to the "release" branch
67 67
 
68 68
 ```bash
69
-git add CHANGELOG.md
69
+git add VERSION CHANGELOG.md
70 70
 git commit -m "Bump version to $VERSION"
71 71
 git push origin bump_$VERSION
72 72
 ```
73 73
 
74 74
 ### 7. Get 2 other maintainers to validate the pull request
75 75
 
76
-### 8. Merge the pull request and apply tags
76
+### 8. Apply tag
77 77
 
78 78
 ```bash
79
-git checkout release
80
-git merge bump_$VERSION
81 79
 git tag -a v$VERSION # Don't forget the v!
82
-git tag -f -a latest
83
-git push
84 80
 git push --tags
85 81
 ```
86 82
 
... ...
@@ -90,28 +89,35 @@ should see the updated docs 5-10 minutes after the merge. The docs
90 90
 will appear on http://docs.docker.io/. For more information about
91 91
 documentation releases, see ``docs/README.md``
92 92
 
93
-### 9. Publish binaries
93
+### 9. Go to github to merge the bump_$VERSION into release
94
+
95
+### 10. Publish binaries
94 96
 
95 97
 To run this you will need access to the release credentials.
96 98
 Get them from [the infrastructure maintainers](
97 99
 https://github.com/dotcloud/docker/blob/master/hack/infrastructure/MAINTAINERS).
98 100
 
99 101
 ```bash
102
+git checkout release
103
+git fetch
104
+git reset --hard origin/release
100 105
 docker build -t docker .
101 106
 docker run  \
102
-	-e AWS_S3_BUCKET=get-nightly.docker.io \
103
-	-e AWS_ACCESS_KEY=$(cat ~/.aws/access_key) \
104
-	-e AWS_SECRET_KEY=$(cat ~/.aws/secret_key) \
105
-	-e GPG_PASSPHRASE=supersecretsesame \
106
-	docker
107
-	hack/release.sh
107
+       -e AWS_S3_BUCKET=test.docker.io \
108
+       -e AWS_ACCESS_KEY=$(cat ~/.aws/access_key) \
109
+       -e AWS_SECRET_KEY=$(cat ~/.aws/secret_key) \
110
+       -e GPG_PASSPHRASE=supersecretsesame \
111
+       -privileged -lxc-conf=lxc.aa_profile=unconfined \
112
+       -t -i \
113
+       docker \
114
+       hack/release.sh
108 115
 ```
109 116
 
110 117
 It will build and upload the binaries on the specified bucket (you should
111
-use get-nightly.docker.io for general testing, and once everything is fine,
118
+use test.docker.io for general testing, and once everything is fine,
112 119
 switch to get.docker.io).
113 120
 
114 121
 
115
-### 10. Rejoice!
122
+### 11. Rejoice!
116 123
 
117 124
 Congratulations! You're done.
... ...
@@ -21,6 +21,14 @@ mountpoint -q $CGROUP ||
21 21
 		exit 1
22 22
 	}
23 23
 
24
+if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security
25
+then
26
+    mount -t securityfs none /sys/kernel/security || {
27
+	echo "Could not mount /sys/kernel/security."
28
+	echo "AppArmor detection and -privileged mode might break."
29
+    }
30
+fi
31
+
24 32
 # Mount the cgroup hierarchies exactly as they are in the parent system.
25 33
 for SUBSYS in $(cut -d: -f2 /proc/1/cgroup)
26 34
 do
... ...
@@ -17,15 +17,12 @@ PORT_GITHUB = 8011      # Buildbot github hook port
17 17
 PORT_MASTER = 9989      # Port where buildbot master listen buildworkers
18 18
 TEST_USER = 'buildbot'  # Credential to authenticate build triggers
19 19
 TEST_PWD = 'docker'     # Credential to authenticate build triggers
20
-BUILDER_NAME = 'docker'
21 20
 GITHUB_DOCKER = 'github.com/dotcloud/docker'
22 21
 BUILDBOT_PATH = '/data/buildbot'
23 22
 DOCKER_PATH = '/go/src/github.com/dotcloud/docker'
24 23
 DOCKER_CI_PATH = '/docker-ci'
25
-BUILDER_PATH = '/data/buildbot/slave/{0}/build'.format(BUILDER_NAME)
26
-PULL_REQUEST_PATH = '/data/buildbot/slave/pullrequest/build'
27 24
 
28
-# Credentials set by setup.sh and Vagrantfile
25
+# Credentials set by setup.sh from deployment.py
29 26
 BUILDBOT_PWD = ''
30 27
 IRC_PWD = ''
31 28
 IRC_CHANNEL = ''
... ...
@@ -45,34 +42,35 @@ c['slavePortnum'] = PORT_MASTER
45 45
 
46 46
 
47 47
 # Schedulers
48
-c['schedulers'] = [ForceScheduler(name='trigger', builderNames=[BUILDER_NAME,
48
+c['schedulers'] = [ForceScheduler(name='trigger', builderNames=['docker',
49 49
     'index','registry','coverage','nightlyrelease'])]
50
-c['schedulers'] += [SingleBranchScheduler(name="all",
51
-    change_filter=filter.ChangeFilter(branch='master'), treeStableTimer=None,
52
-    builderNames=[BUILDER_NAME])]
50
+c['schedulers'] += [SingleBranchScheduler(name="all", treeStableTimer=None,
51
+    change_filter=filter.ChangeFilter(branch='master',
52
+    repository='https://github.com/dotcloud/docker'), builderNames=['docker'])]
53 53
 c['schedulers'] += [SingleBranchScheduler(name='pullrequest',
54 54
     change_filter=filter.ChangeFilter(category='github_pullrequest'), treeStableTimer=None,
55 55
     builderNames=['pullrequest'])]
56
-c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['nightlyrelease'],
57
-    hour=7, minute=00)]
56
+c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['nightlyrelease',
57
+    'coverage'], hour=7, minute=00)]
58 58
 c['schedulers'] += [Nightly(name='every4hrs', branch=None, builderNames=['registry','index'],
59 59
     hour=range(0,24,4), minute=15)]
60 60
 
61 61
 # Builders
62 62
 # Docker commit test
63
+test_cmd = ('docker run -privileged mzdaniel/test_docker hack/dind'
64
+    ' test_docker.sh %(src::revision)s')
63 65
 factory = BuildFactory()
64 66
 factory.addStep(ShellCommand(description='Docker', logEnviron=False,
65
-    usePTY=True, command=['sh', '-c', Interpolate(
66
-    '{0}/docker-test/test_docker.sh %(src::revision)s'.format(DOCKER_CI_PATH))]))
67
+    usePTY=True, command=["sh", "-c", Interpolate(test_cmd)]))
67 68
 c['builders'] = [BuilderConfig(name='docker',slavenames=['buildworker'],
68 69
     factory=factory)]
69 70
 
70 71
 # Docker pull request test
72
+test_cmd = ('docker run -privileged mzdaniel/test_docker hack/dind'
73
+    ' test_docker.sh %(src::revision)s %(src::repository)s %(src::branch)s')
71 74
 factory = BuildFactory()
72 75
 factory.addStep(ShellCommand(description='pull_request', logEnviron=False,
73
-    usePTY=True, command=['sh', '-c', Interpolate(
74
-    '{0}/docker-test/test_docker.sh %(src::revision)s %(src::repository)s'
75
-    ' %(src::branch)s'.format(DOCKER_CI_PATH))]))
76
+    usePTY=True, command=["sh", "-c", Interpolate(test_cmd)]))
76 77
 c['builders'] += [BuilderConfig(name='pullrequest',slavenames=['buildworker'],
77 78
     factory=factory)]
78 79
 
... ...
@@ -97,17 +95,16 @@ c['builders'] += [BuilderConfig(name='registry',slavenames=['buildworker'],
97 97
 factory = BuildFactory()
98 98
 factory.addStep(ShellCommand(description='index', logEnviron=False,
99 99
     command='. {0}/master/credentials.cfg; '
100
-    '{1}/testing/functionaltests/test_index.py'.format(BUILDBOT_PATH,
101
-    DOCKER_PATH), usePTY=True))
100
+    '/docker-ci/functionaltests/test_index.py'.format(BUILDBOT_PATH),
101
+    usePTY=True))
102 102
 c['builders'] += [BuilderConfig(name='index',slavenames=['buildworker'],
103 103
     factory=factory)]
104 104
 
105 105
 # Docker nightly release
106
-nightlyrelease_cmd = ('docker run -i -t -privileged -lxc-conf=lxc.aa_profile=unconfined'
107
-    ' -e AWS_S3_BUCKET=test.docker.io dockerbuilder')
108 106
 factory = BuildFactory()
109
-factory.addStep(ShellCommand(description='NightlyRelease',logEnviron=False,usePTY=True,
110
-    command=nightlyrelease_cmd))
107
+factory.addStep(ShellCommand(description='NightlyRelease', logEnviron=False,
108
+    usePTY=True, command='docker run -privileged'
109
+    ' -e AWS_S3_BUCKET=test.docker.io dockerbuilder'))
111 110
 c['builders'] += [BuilderConfig(name='nightlyrelease',slavenames=['buildworker'],
112 111
     factory=factory)]
113 112
 
... ...
@@ -135,16 +135,17 @@ sudo('curl -s https://phantomjs.googlecode.com/files/'
135 135
     'phantomjs-1.9.1-linux-x86_64.tar.bz2 | tar jx -C /usr/bin'
136 136
     ' --strip-components=2 phantomjs-1.9.1-linux-x86_64/bin/phantomjs')
137 137
 
138
-#### FIXME. Temporarily install docker with proper apparmor handling
139
-sudo('stop docker')
140
-sudo('wget -q -O /usr/bin/docker http://test.docker.io/test/docker')
141
-sudo('start docker')
138
+# Preventively reboot docker-ci daily
139
+sudo('ln -s /sbin/reboot /etc/cron.daily')
142 140
 
143 141
 # Build docker-ci containers
144 142
 sudo('cd {}; docker build -t docker .'.format(DOCKER_PATH))
145 143
 sudo('cd {}/nightlyrelease; docker build -t dockerbuilder .'.format(
146 144
     DOCKER_CI_PATH))
147 145
 
146
+# Download docker-ci testing container
147
+sudo('docker pull mzdaniel/test_docker')
148
+
148 149
 # Setup buildbot
149 150
 sudo('mkdir /data')
150 151
 sudo('{0}/setup.sh root {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10}'
151 152
new file mode 100644
... ...
@@ -0,0 +1,30 @@
0
+# VERSION:        0.3
1
+# DOCKER-VERSION  0.6.3
2
+# AUTHOR:         Daniel Mizyrycki <daniel@dotcloud.com>
3
+# DESCRIPTION:    Testing docker PRs and commits on top of master using
4
+# REFERENCES:     This code reuses the excellent implementation of
5
+#                 Docker in Docker made by Jerome Petazzoni.
6
+#                 https://github.com/jpetazzo/dind
7
+# COMMENTS:
8
+#   This Dockerfile adapts /Dockerfile to enable docker PRs and commits testing
9
+#   Optional arguments:
10
+#       [commit]  (default: 'HEAD')
11
+#       [repo]    (default: 'http://github.com/dotcloud/docker')
12
+#       [branch]  (default: 'master')
13
+# TO_BUILD:       docker build -t test_docker .
14
+# TO_RUN:         docker run -privileged test_docker hack/dind test_docker.sh [commit] [repo] [branch]
15
+
16
+from docker
17
+maintainer Daniel Mizyrycki <daniel@dotcloud.com>
18
+
19
+# Setup go environment. Extracted from /Dockerfile
20
+env     CGO_ENABLED 0
21
+env     GOROOT  /goroot
22
+env     PATH    $PATH:/goroot/bin
23
+env     GOPATH  /go:/go/src/github.com/dotcloud/docker/vendor
24
+volume  /var/lib/docker
25
+workdir /go/src/github.com/dotcloud/docker
26
+
27
+# Add test_docker.sh
28
+add test_docker.sh /usr/bin/test_docker.sh
29
+run chmod +x /usr/bin/test_docker.sh
... ...
@@ -5,17 +5,11 @@ COMMIT=${1-HEAD}
5 5
 REPO=${2-http://github.com/dotcloud/docker}
6 6
 BRANCH=${3-master}
7 7
 
8
-# Generate a random string of $1 characters
9
-function random {
10
-    cat /dev/urandom | tr -cd 'a-f0-9' | head -c $1
11
-}
12
-
13 8
 # Compute test paths
14
-BASE_PATH=`pwd`/test_docker_$(random 12)
15
-DOCKER_PATH=$BASE_PATH/go/src/github.com/dotcloud/docker
16
-export GOPATH=$BASE_PATH/go:$DOCKER_PATH/vendor
9
+DOCKER_PATH=/go/src/github.com/dotcloud/docker
17 10
 
18 11
 # Fetch latest master
12
+rm -rf /go
19 13
 mkdir -p $DOCKER_PATH
20 14
 cd $DOCKER_PATH
21 15
 git init .
... ...
@@ -23,12 +17,21 @@ git fetch -q http://github.com/dotcloud/docker master
23 23
 git reset --hard FETCH_HEAD
24 24
 
25 25
 # Merge commit
26
+#echo FIXME. Temporarily skip TestPrivilegedCanMount until DinD works reliable on AWS
27
+git pull -q https://github.com/mzdaniel/docker.git dind-aws || exit 1
28
+
29
+# Merge commit in top of master
26 30
 git fetch -q "$REPO" "$BRANCH"
27 31
 git merge --no-edit $COMMIT || exit 1
28 32
 
29 33
 # Test commit
30 34
 go test -v; exit_status=$?
31 35
 
36
+# Display load if test fails
37
+if [ $exit_status -eq 1 ] ; then
38
+    uptime; echo; free
39
+fi
40
+
32 41
 # Cleanup testing directory
33 42
 rm -rf $BASE_PATH
34 43
 
... ...
@@ -11,7 +11,7 @@
11 11
 #         "GPG_PASSPHRASE='Test_docker_GPG_passphrase_signature'
12 12
 #         "INDEX_AUTH='Encripted_index_authentication' }
13 13
 # TO_BUILD:       docker build -t dockerbuilder .
14
-# TO_RELEASE:     docker run -i -t -privileged -lxc-conf="lxc.aa_profile = unconfined" -e AWS_S3_BUCKET="test.docker.io" dockerbuilder
14
+# TO_RELEASE:     docker run -i -t -privileged  -e AWS_S3_BUCKET="test.docker.io" dockerbuilder
15 15
 
16 16
 from docker
17 17
 maintainer Daniel Mizyrycki <daniel@dotcloud.com>
... ...
@@ -23,9 +23,6 @@ run apt-get update; apt-get install -y -q wget python2.7
23 23
 # Add production docker binary
24 24
 run wget -q -O /usr/bin/docker http://get.docker.io/builds/Linux/x86_64/docker-latest; chmod +x /usr/bin/docker
25 25
 
26
-#### FIXME. Temporarily install docker with proper apparmor handling
27
-run wget -q -O /usr/bin/docker http://test.docker.io/test/docker; chmod +x /usr/bin/docker
28
-
29 26
 # Add proto docker builder
30 27
 add ./dockerbuild /usr/bin/dockerbuild
31 28
 run chmod +x /usr/bin/dockerbuild
... ...
@@ -13,10 +13,6 @@ cd /
13 13
 git clone -q http://github.com/dotcloud/docker /go/src/github.com/dotcloud/docker
14 14
 cd /go/src/github.com/dotcloud/docker
15 15
 
16
-echo FIXME. Temporarily add Jerome changeset with proper apparmor handling
17
-git fetch  http://github.com/jpetazzo/docker escape-apparmor-confinement:escape-apparmor-confinement
18
-git rebase --onto master master escape-apparmor-confinement
19
-
20 16
 # Launch docker daemon using dind inside the container
21 17
 ./hack/dind /usr/bin/docker -d &
22 18
 sleep 5
... ...
@@ -28,7 +24,17 @@ date > timestamp
28 28
 docker build -t docker .
29 29
 
30 30
 # Run Docker unittests binary and Ubuntu package
31
-docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh || exit 1
31
+docker run -privileged docker hack/make.sh
32
+exit_status=$?
33
+
34
+# Display load if test fails
35
+if [ $exit_status -eq 1 ] ; then
36
+    uptime; echo; free
37
+    exit 1
38
+fi
39
+
40
+# Commit binary and ubuntu bundles for release
41
+docker commit -run '{"Env": ["PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"], "WorkingDir": "/go/src/github.com/dotcloud/docker"}' $(docker ps -l -q) release
32 42
 
33 43
 # Turn debug off to load credentials from the environment
34 44
 set +x
... ...
@@ -36,7 +42,6 @@ eval $(cat /root/release_credentials.json  | python -c '
36 36
 import sys,json,base64;
37 37
 d=json.loads(base64.b64decode(sys.stdin.read()));
38 38
 exec("""for k in d: print "export {0}=\\"{1}\\"".format(k,d[k])""")')
39
-echo '{"https://index.docker.io/v1/":{"auth":"'$INDEX_AUTH'","email":"engineering@dotcloud.com"}}' > /.dockercfg
40 39
 set -x
41 40
 
42 41
 # Push docker nightly
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"encoding/hex"
6 6
 	"encoding/json"
7 7
 	"fmt"
8
+	"github.com/dotcloud/docker/archive"
8 9
 	"github.com/dotcloud/docker/utils"
9 10
 	"io"
10 11
 	"io/ioutil"
... ...
@@ -72,7 +73,7 @@ func LoadImage(root string) (*Image, error) {
72 72
 	return img, nil
73 73
 }
74 74
 
75
-func StoreImage(img *Image, jsonData []byte, layerData Archive, root string) error {
75
+func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root string) error {
76 76
 	// Check that root doesn't already exist
77 77
 	if _, err := os.Stat(root); err == nil {
78 78
 		return fmt.Errorf("Image %s already exists", img.ID)
... ...
@@ -89,7 +90,7 @@ func StoreImage(img *Image, jsonData []byte, layerData Archive, root string) err
89 89
 	if layerData != nil {
90 90
 		start := time.Now()
91 91
 		utils.Debugf("Start untar layer")
92
-		if err := Untar(layerData, layer); err != nil {
92
+		if err := archive.Untar(layerData, layer); err != nil {
93 93
 			return err
94 94
 		}
95 95
 		utils.Debugf("Untar time: %vs", time.Now().Sub(start).Seconds())
... ...
@@ -162,12 +163,12 @@ func MountAUFS(ro []string, rw string, target string) error {
162 162
 }
163 163
 
164 164
 // TarLayer returns a tar archive of the image's filesystem layer.
165
-func (image *Image) TarLayer(compression Compression) (Archive, error) {
165
+func (image *Image) TarLayer(compression archive.Compression) (archive.Archive, error) {
166 166
 	layerPath, err := image.layer()
167 167
 	if err != nil {
168 168
 		return nil, err
169 169
 	}
170
-	return Tar(layerPath, compression)
170
+	return archive.Tar(layerPath, compression)
171 171
 }
172 172
 
173 173
 func (image *Image) Mount(root, rw string) error {
... ...
@@ -11,7 +11,6 @@ lxc.utsname = {{.Config.Hostname}}
11 11
 {{else}}
12 12
 lxc.utsname = {{.Id}}
13 13
 {{end}}
14
-#lxc.aa_profile = unconfined
15 14
 
16 15
 {{if .Config.NetworkDisabled}}
17 16
 # network is disabled (-n=false)
... ...
@@ -46,7 +45,7 @@ lxc.console = none
46 46
 # no controlling tty at all
47 47
 lxc.tty = 1
48 48
 
49
-{{if .Config.Privileged}}
49
+{{if (getHostConfig .).Privileged}}
50 50
 lxc.cgroup.devices.allow = a 
51 51
 {{else}}
52 52
 # no implicit access to devices
... ...
@@ -66,7 +65,7 @@ lxc.cgroup.devices.allow = c 4:1 rwm
66 66
 lxc.cgroup.devices.allow = c 1:9 rwm
67 67
 lxc.cgroup.devices.allow = c 1:8 rwm
68 68
 
69
-# /dev/pts/* - pts namespaces are "coming soon"
69
+# /dev/pts/ - pts namespaces are "coming soon"
70 70
 lxc.cgroup.devices.allow = c 136:* rwm
71 71
 lxc.cgroup.devices.allow = c 5:2 rwm
72 72
 
... ...
@@ -97,6 +96,9 @@ lxc.mount.entry = shm {{$ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec
97 97
 # Inject dockerinit
98 98
 lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/.dockerinit none bind,ro 0 0
99 99
 
100
+# Inject env
101
+lxc.mount.entry = {{.EnvConfigPath}} {{$ROOTFS}}/.dockerenv none bind,ro 0 0
102
+
100 103
 # In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
101 104
 lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
102 105
 {{if .Volumes}}
... ...
@@ -106,8 +108,13 @@ lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,{{ if ind
106 106
 {{end}}
107 107
 {{end}}
108 108
 
109
-{{if .Config.Privileged}}
109
+{{if (getHostConfig .).Privileged}}
110 110
 # retain all capabilities; no lxc.cap.drop line
111
+{{if (getCapabilities .).AppArmor}}
112
+lxc.aa_profile = unconfined
113
+{{else}}
114
+#lxc.aa_profile = unconfined
115
+{{end}}
111 116
 {{else}}
112 117
 # drop linux capabilities (apply mainly to the user root in the container)
113 118
 #  (Note: 'lxc.cap.keep' is coming soon and should replace this under the
... ...
@@ -127,18 +134,15 @@ lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
127 127
 {{if .Config.CpuShares}}
128 128
 lxc.cgroup.cpu.shares = {{.Config.CpuShares}}
129 129
 {{end}}
130
-`
131 130
 
132
-const LxcHostConfigTemplate = `
133
-{{if .LxcConf}}
134
-{{range $pair := .LxcConf}}
131
+{{if (getHostConfig .).LxcConf}}
132
+{{range $pair := (getHostConfig .).LxcConf}}
135 133
 {{$pair.Key}} = {{$pair.Value}}
136 134
 {{end}}
137 135
 {{end}}
138 136
 `
139 137
 
140 138
 var LxcTemplateCompiled *template.Template
141
-var LxcHostConfigTemplateCompiled *template.Template
142 139
 
143 140
 func getMemorySwap(config *Config) int64 {
144 141
 	// By default, MemorySwap is set to twice the size of RAM.
... ...
@@ -149,17 +153,23 @@ func getMemorySwap(config *Config) int64 {
149 149
 	return config.Memory * 2
150 150
 }
151 151
 
152
+func getHostConfig(container *Container) *HostConfig {
153
+	return container.hostConfig
154
+}
155
+
156
+func getCapabilities(container *Container) *Capabilities {
157
+	return container.runtime.capabilities
158
+}
159
+
152 160
 func init() {
153 161
 	var err error
154 162
 	funcMap := template.FuncMap{
155
-		"getMemorySwap": getMemorySwap,
163
+		"getMemorySwap":   getMemorySwap,
164
+		"getHostConfig":   getHostConfig,
165
+		"getCapabilities": getCapabilities,
156 166
 	}
157 167
 	LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
158 168
 	if err != nil {
159 169
 		panic(err)
160 170
 	}
161
-	LxcHostConfigTemplateCompiled, err = template.New("lxc-hostconfig").Funcs(funcMap).Parse(LxcHostConfigTemplate)
162
-	if err != nil {
163
-		panic(err)
164
-	}
165 171
 }
... ...
@@ -11,8 +11,8 @@ type NameChecker interface {
11 11
 }
12 12
 
13 13
 var (
14
-	colors  = [...]string{"white", "silver", "gray", "black", "blue", "green", "cyan", "yellow", "gold", "orange", "brown", "red", "violet", "pink", "magenta", "purple"}
15
-	animals = [...]string{"ant", "bird", "cat", "chicken", "cow", "dog", "fish", "fox", "horse", "lion", "monkey", "pig", "sheep", "tiger", "whale", "wolf"}
14
+	colors  = [...]string{"white", "silver", "gray", "black", "blue", "green", "cyan", "yellow", "gold", "orange", "brown", "red", "violet", "pink", "magenta", "purple", "maroon", "crimson", "plum", "fuchsia", "lavender", "slate", "navy", "azure", "aqua", "olive", "teal", "lime", "beige", "tan", "sienna"}
15
+  animals = [...]string{"ant", "bear", "bird", "cat", "chicken", "cow", "deer", "dog", "donkey", "duck", "fish", "fox", "frog", "horse", "kangaroo", "koala", "lemur", "lion", "lizard", "monkey", "octopus", "pig", "shark", "sheep", "sloth", "spider", "squirrel", "tiger", "toad", "weasel", "whale", "wolf"}
16 16
 )
17 17
 
18 18
 func GenerateRandomName(checker NameChecker) (string, error) {
19 19
deleted file mode 100644
... ...
@@ -1,548 +0,0 @@
1
-package netlink
2
-
3
-import (
4
-	"encoding/binary"
5
-	"fmt"
6
-	"net"
7
-	"syscall"
8
-	"unsafe"
9
-)
10
-
11
-var nextSeqNr int
12
-
13
-func nativeEndian() binary.ByteOrder {
14
-	var x uint32 = 0x01020304
15
-	if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
16
-		return binary.BigEndian
17
-	}
18
-	return binary.LittleEndian
19
-}
20
-
21
-func getSeq() int {
22
-	nextSeqNr = nextSeqNr + 1
23
-	return nextSeqNr
24
-}
25
-
26
-func getIpFamily(ip net.IP) int {
27
-	if len(ip) <= net.IPv4len {
28
-		return syscall.AF_INET
29
-	}
30
-	if ip.To4() != nil {
31
-		return syscall.AF_INET
32
-	}
33
-	return syscall.AF_INET6
34
-}
35
-
36
-type NetlinkRequestData interface {
37
-	ToWireFormat() []byte
38
-}
39
-
40
-type IfInfomsg struct {
41
-	syscall.IfInfomsg
42
-}
43
-
44
-func newIfInfomsg(family int) *IfInfomsg {
45
-	msg := &IfInfomsg{}
46
-	msg.Family = uint8(family)
47
-	msg.Type = uint16(0)
48
-	msg.Index = int32(0)
49
-	msg.Flags = uint32(0)
50
-	msg.Change = uint32(0)
51
-
52
-	return msg
53
-}
54
-
55
-func (msg *IfInfomsg) ToWireFormat() []byte {
56
-	native := nativeEndian()
57
-
58
-	len := syscall.SizeofIfInfomsg
59
-	b := make([]byte, len)
60
-	b[0] = msg.Family
61
-	b[1] = 0
62
-	native.PutUint16(b[2:4], msg.Type)
63
-	native.PutUint32(b[4:8], uint32(msg.Index))
64
-	native.PutUint32(b[8:12], msg.Flags)
65
-	native.PutUint32(b[12:16], msg.Change)
66
-	return b
67
-}
68
-
69
-type IfAddrmsg struct {
70
-	syscall.IfAddrmsg
71
-}
72
-
73
-func newIfAddrmsg(family int) *IfAddrmsg {
74
-	msg := &IfAddrmsg{}
75
-	msg.Family = uint8(family)
76
-	msg.Prefixlen = uint8(0)
77
-	msg.Flags = uint8(0)
78
-	msg.Scope = uint8(0)
79
-	msg.Index = uint32(0)
80
-
81
-	return msg
82
-}
83
-
84
-func (msg *IfAddrmsg) ToWireFormat() []byte {
85
-	native := nativeEndian()
86
-
87
-	len := syscall.SizeofIfAddrmsg
88
-	b := make([]byte, len)
89
-	b[0] = msg.Family
90
-	b[1] = msg.Prefixlen
91
-	b[2] = msg.Flags
92
-	b[3] = msg.Scope
93
-	native.PutUint32(b[4:8], msg.Index)
94
-	return b
95
-}
96
-
97
-type RtMsg struct {
98
-	syscall.RtMsg
99
-}
100
-
101
-func newRtMsg(family int) *RtMsg {
102
-	msg := &RtMsg{}
103
-	msg.Family = uint8(family)
104
-	msg.Table = syscall.RT_TABLE_MAIN
105
-	msg.Scope = syscall.RT_SCOPE_UNIVERSE
106
-	msg.Protocol = syscall.RTPROT_BOOT
107
-	msg.Type = syscall.RTN_UNICAST
108
-
109
-	return msg
110
-}
111
-
112
-func (msg *RtMsg) ToWireFormat() []byte {
113
-	native := nativeEndian()
114
-
115
-	len := syscall.SizeofRtMsg
116
-	b := make([]byte, len)
117
-	b[0] = msg.Family
118
-	b[1] = msg.Dst_len
119
-	b[2] = msg.Src_len
120
-	b[3] = msg.Tos
121
-	b[4] = msg.Table
122
-	b[5] = msg.Protocol
123
-	b[6] = msg.Scope
124
-	b[7] = msg.Type
125
-	native.PutUint32(b[8:12], msg.Flags)
126
-	return b
127
-}
128
-
129
-func rtaAlignOf(attrlen int) int {
130
-	return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
131
-}
132
-
133
-type RtAttr struct {
134
-	syscall.RtAttr
135
-	Data []byte
136
-}
137
-
138
-func newRtAttr(attrType int, data []byte) *RtAttr {
139
-	attr := &RtAttr{}
140
-	attr.Type = uint16(attrType)
141
-	attr.Data = data
142
-
143
-	return attr
144
-}
145
-
146
-func (attr *RtAttr) ToWireFormat() []byte {
147
-	native := nativeEndian()
148
-
149
-	len := syscall.SizeofRtAttr + len(attr.Data)
150
-	b := make([]byte, rtaAlignOf(len))
151
-	native.PutUint16(b[0:2], uint16(len))
152
-	native.PutUint16(b[2:4], attr.Type)
153
-	for i, d := range attr.Data {
154
-		b[4+i] = d
155
-	}
156
-
157
-	return b
158
-}
159
-
160
-type NetlinkRequest struct {
161
-	syscall.NlMsghdr
162
-	Data []NetlinkRequestData
163
-}
164
-
165
-func (rr *NetlinkRequest) ToWireFormat() []byte {
166
-	native := nativeEndian()
167
-
168
-	length := rr.Len
169
-	dataBytes := make([][]byte, len(rr.Data))
170
-	for i, data := range rr.Data {
171
-		dataBytes[i] = data.ToWireFormat()
172
-		length = length + uint32(len(dataBytes[i]))
173
-	}
174
-	b := make([]byte, length)
175
-	native.PutUint32(b[0:4], length)
176
-	native.PutUint16(b[4:6], rr.Type)
177
-	native.PutUint16(b[6:8], rr.Flags)
178
-	native.PutUint32(b[8:12], rr.Seq)
179
-	native.PutUint32(b[12:16], rr.Pid)
180
-
181
-	i := 16
182
-	for _, data := range dataBytes {
183
-		for _, dataByte := range data {
184
-			b[i] = dataByte
185
-			i = i + 1
186
-		}
187
-	}
188
-	return b
189
-}
190
-
191
-func (rr *NetlinkRequest) AddData(data NetlinkRequestData) {
192
-	rr.Data = append(rr.Data, data)
193
-}
194
-
195
-func newNetlinkRequest(proto, flags int) *NetlinkRequest {
196
-	rr := &NetlinkRequest{}
197
-	rr.Len = uint32(syscall.NLMSG_HDRLEN)
198
-	rr.Type = uint16(proto)
199
-	rr.Flags = syscall.NLM_F_REQUEST | uint16(flags)
200
-	rr.Seq = uint32(getSeq())
201
-	return rr
202
-}
203
-
204
-type NetlinkSocket struct {
205
-	fd  int
206
-	lsa syscall.SockaddrNetlink
207
-}
208
-
209
-func getNetlinkSocket() (*NetlinkSocket, error) {
210
-	fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE)
211
-	if err != nil {
212
-		return nil, err
213
-	}
214
-	s := &NetlinkSocket{
215
-		fd: fd,
216
-	}
217
-	s.lsa.Family = syscall.AF_NETLINK
218
-	if err := syscall.Bind(fd, &s.lsa); err != nil {
219
-		syscall.Close(fd)
220
-		return nil, err
221
-	}
222
-
223
-	return s, nil
224
-}
225
-
226
-func (s *NetlinkSocket) Close() {
227
-	syscall.Close(s.fd)
228
-}
229
-
230
-func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
231
-	if err := syscall.Sendto(s.fd, request.ToWireFormat(), 0, &s.lsa); err != nil {
232
-		return err
233
-	}
234
-	return nil
235
-}
236
-
237
-func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) {
238
-	rb := make([]byte, syscall.Getpagesize())
239
-	nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
240
-	if err != nil {
241
-		return nil, err
242
-	}
243
-	if nr < syscall.NLMSG_HDRLEN {
244
-		return nil, fmt.Errorf("Got short response from netlink")
245
-	}
246
-	rb = rb[:nr]
247
-	return syscall.ParseNetlinkMessage(rb)
248
-}
249
-
250
-func (s *NetlinkSocket) GetPid() (uint32, error) {
251
-	lsa, err := syscall.Getsockname(s.fd)
252
-	if err != nil {
253
-		return 0, err
254
-	}
255
-	switch v := lsa.(type) {
256
-	case *syscall.SockaddrNetlink:
257
-		return v.Pid, nil
258
-	}
259
-	return 0, fmt.Errorf("Wrong socket type")
260
-}
261
-
262
-func (s *NetlinkSocket) HandleAck(seq uint32) error {
263
-	native := nativeEndian()
264
-
265
-	pid, err := s.GetPid()
266
-	if err != nil {
267
-		return err
268
-	}
269
-
270
-done:
271
-	for {
272
-		msgs, err := s.Recieve()
273
-		if err != nil {
274
-			return err
275
-		}
276
-		for _, m := range msgs {
277
-			if m.Header.Seq != seq {
278
-				return fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq)
279
-			}
280
-			if m.Header.Pid != pid {
281
-				return fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
282
-			}
283
-			if m.Header.Type == syscall.NLMSG_DONE {
284
-				break done
285
-			}
286
-			if m.Header.Type == syscall.NLMSG_ERROR {
287
-				error := int32(native.Uint32(m.Data[0:4]))
288
-				if error == 0 {
289
-					break done
290
-				}
291
-				return syscall.Errno(-error)
292
-			}
293
-		}
294
-	}
295
-
296
-	return nil
297
-}
298
-
299
-// Add a new default gateway. Identical to:
300
-// ip route add default via $ip
301
-func AddDefaultGw(ip net.IP) error {
302
-	s, err := getNetlinkSocket()
303
-	if err != nil {
304
-		return err
305
-	}
306
-	defer s.Close()
307
-
308
-	family := getIpFamily(ip)
309
-
310
-	wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
311
-
312
-	msg := newRtMsg(family)
313
-	wb.AddData(msg)
314
-
315
-	var ipData []byte
316
-	if family == syscall.AF_INET {
317
-		ipData = ip.To4()
318
-	} else {
319
-		ipData = ip.To16()
320
-	}
321
-
322
-	gateway := newRtAttr(syscall.RTA_GATEWAY, ipData)
323
-
324
-	wb.AddData(gateway)
325
-
326
-	if err := s.Send(wb); err != nil {
327
-		return err
328
-	}
329
-
330
-	return s.HandleAck(wb.Seq)
331
-
332
-}
333
-
334
-// Bring up a particular network interface
335
-func NetworkLinkUp(iface *net.Interface) error {
336
-	s, err := getNetlinkSocket()
337
-	if err != nil {
338
-		return err
339
-	}
340
-	defer s.Close()
341
-
342
-	wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
343
-
344
-	msg := newIfInfomsg(syscall.AF_UNSPEC)
345
-	msg.Change = syscall.IFF_UP
346
-	msg.Flags = syscall.IFF_UP
347
-	msg.Index = int32(iface.Index)
348
-	wb.AddData(msg)
349
-
350
-	if err := s.Send(wb); err != nil {
351
-		return err
352
-	}
353
-
354
-	return s.HandleAck(wb.Seq)
355
-}
356
-
357
-// Add an Ip address to an interface. This is identical to:
358
-// ip addr add $ip/$ipNet dev $iface
359
-func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
360
-	s, err := getNetlinkSocket()
361
-	if err != nil {
362
-		return err
363
-	}
364
-	defer s.Close()
365
-
366
-	family := getIpFamily(ip)
367
-
368
-	wb := newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
369
-
370
-	msg := newIfAddrmsg(family)
371
-	msg.Index = uint32(iface.Index)
372
-	prefixLen, _ := ipNet.Mask.Size()
373
-	msg.Prefixlen = uint8(prefixLen)
374
-	wb.AddData(msg)
375
-
376
-	var ipData []byte
377
-	if family == syscall.AF_INET {
378
-		ipData = ip.To4()
379
-	} else {
380
-		ipData = ip.To16()
381
-	}
382
-
383
-	localData := newRtAttr(syscall.IFA_LOCAL, ipData)
384
-	wb.AddData(localData)
385
-
386
-	addrData := newRtAttr(syscall.IFA_ADDRESS, ipData)
387
-	wb.AddData(addrData)
388
-
389
-	if err := s.Send(wb); err != nil {
390
-		return err
391
-	}
392
-
393
-	return s.HandleAck(wb.Seq)
394
-}
395
-
396
-func zeroTerminated(s string) []byte {
397
-	bytes := make([]byte, len(s)+1)
398
-	for i := 0; i < len(s); i++ {
399
-		bytes[i] = s[i]
400
-	}
401
-	bytes[len(s)] = 0
402
-	return bytes
403
-}
404
-
405
-func nonZeroTerminated(s string) []byte {
406
-	bytes := make([]byte, len(s))
407
-	for i := 0; i < len(s); i++ {
408
-		bytes[i] = s[i]
409
-	}
410
-	return bytes
411
-}
412
-
413
-// Add a new network link of a specified type. This is identical to
414
-// running: ip add link $name type $linkType
415
-func NetworkLinkAdd(name string, linkType string) error {
416
-	s, err := getNetlinkSocket()
417
-	if err != nil {
418
-		return err
419
-	}
420
-	defer s.Close()
421
-
422
-	wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
423
-
424
-	msg := newIfInfomsg(syscall.AF_UNSPEC)
425
-	wb.AddData(msg)
426
-
427
-	nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
428
-	wb.AddData(nameData)
429
-
430
-	IFLA_INFO_KIND := 1
431
-
432
-	kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType))
433
-
434
-	infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat())
435
-	wb.AddData(infoData)
436
-
437
-	if err := s.Send(wb); err != nil {
438
-		return err
439
-	}
440
-
441
-	return s.HandleAck(wb.Seq)
442
-}
443
-
444
-// Returns an array of IPNet for all the currently routed subnets on ipv4
445
-// This is similar to the first column of "ip route" output
446
-func NetworkGetRoutes() ([]*net.IPNet, error) {
447
-	native := nativeEndian()
448
-
449
-	s, err := getNetlinkSocket()
450
-	if err != nil {
451
-		return nil, err
452
-	}
453
-	defer s.Close()
454
-
455
-	wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
456
-
457
-	msg := newIfInfomsg(syscall.AF_UNSPEC)
458
-	wb.AddData(msg)
459
-
460
-	if err := s.Send(wb); err != nil {
461
-		return nil, err
462
-	}
463
-
464
-	pid, err := s.GetPid()
465
-	if err != nil {
466
-		return nil, err
467
-	}
468
-
469
-	res := make([]*net.IPNet, 0)
470
-
471
-done:
472
-	for {
473
-		msgs, err := s.Recieve()
474
-		if err != nil {
475
-			return nil, err
476
-		}
477
-		for _, m := range msgs {
478
-			if m.Header.Seq != wb.Seq {
479
-				return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
480
-			}
481
-			if m.Header.Pid != pid {
482
-				return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
483
-			}
484
-			if m.Header.Type == syscall.NLMSG_DONE {
485
-				break done
486
-			}
487
-			if m.Header.Type == syscall.NLMSG_ERROR {
488
-				error := int32(native.Uint32(m.Data[0:4]))
489
-				if error == 0 {
490
-					break done
491
-				}
492
-				return nil, syscall.Errno(-error)
493
-			}
494
-			if m.Header.Type != syscall.RTM_NEWROUTE {
495
-				continue
496
-			}
497
-
498
-			var iface *net.Interface = nil
499
-			var ipNet *net.IPNet = nil
500
-
501
-			msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0]))
502
-
503
-			if msg.Flags&syscall.RTM_F_CLONED != 0 {
504
-				// Ignore cloned routes
505
-				continue
506
-			}
507
-
508
-			if msg.Table != syscall.RT_TABLE_MAIN {
509
-				// Ignore non-main tables
510
-				continue
511
-			}
512
-
513
-			if msg.Family != syscall.AF_INET {
514
-				// Ignore non-ipv4 routes
515
-				continue
516
-			}
517
-
518
-			if msg.Dst_len == 0 {
519
-				// Ignore default routes
520
-				continue
521
-			}
522
-
523
-			attrs, err := syscall.ParseNetlinkRouteAttr(&m)
524
-			if err != nil {
525
-				return nil, err
526
-			}
527
-			for _, attr := range attrs {
528
-				switch attr.Attr.Type {
529
-				case syscall.RTA_DST:
530
-					ip := attr.Value
531
-					ipNet = &net.IPNet{
532
-						IP:   ip,
533
-						Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)),
534
-					}
535
-				case syscall.RTA_OIF:
536
-					index := int(native.Uint32(attr.Value[0:4]))
537
-					iface, _ = net.InterfaceByIndex(index)
538
-					_ = iface
539
-				}
540
-			}
541
-			if ipNet != nil {
542
-				res = append(res, ipNet)
543
-			}
544
-		}
545
-	}
546
-
547
-	return res, nil
548
-}
549 1
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+package netlink
1
+
2
+import (
3
+	"fmt"
4
+	"net"
5
+)
6
+
7
+func NetworkGetRoutes() ([]*net.IPNet, error) {
8
+	return nil, fmt.Errorf("Not implemented")
9
+}
10
+
11
+
12
+func NetworkLinkAdd(name string, linkType string) error {
13
+	return fmt.Errorf("Not implemented")
14
+}
15
+
16
+func NetworkLinkUp(iface *net.Interface) error {
17
+	return fmt.Errorf("Not implemented")
18
+}
19
+
20
+
21
+func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
22
+	return fmt.Errorf("Not implemented")
23
+}
24
+
25
+func AddDefaultGw(ip net.IP) error {
26
+	return fmt.Errorf("Not implemented")
27
+
28
+}
0 29
new file mode 100644
... ...
@@ -0,0 +1,548 @@
0
+package netlink
1
+
2
+import (
3
+	"encoding/binary"
4
+	"fmt"
5
+	"net"
6
+	"syscall"
7
+	"unsafe"
8
+)
9
+
10
+var nextSeqNr int
11
+
12
+func nativeEndian() binary.ByteOrder {
13
+	var x uint32 = 0x01020304
14
+	if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
15
+		return binary.BigEndian
16
+	}
17
+	return binary.LittleEndian
18
+}
19
+
20
+func getSeq() int {
21
+	nextSeqNr = nextSeqNr + 1
22
+	return nextSeqNr
23
+}
24
+
25
+func getIpFamily(ip net.IP) int {
26
+	if len(ip) <= net.IPv4len {
27
+		return syscall.AF_INET
28
+	}
29
+	if ip.To4() != nil {
30
+		return syscall.AF_INET
31
+	}
32
+	return syscall.AF_INET6
33
+}
34
+
35
+type NetlinkRequestData interface {
36
+	ToWireFormat() []byte
37
+}
38
+
39
+type IfInfomsg struct {
40
+	syscall.IfInfomsg
41
+}
42
+
43
+func newIfInfomsg(family int) *IfInfomsg {
44
+	msg := &IfInfomsg{}
45
+	msg.Family = uint8(family)
46
+	msg.Type = uint16(0)
47
+	msg.Index = int32(0)
48
+	msg.Flags = uint32(0)
49
+	msg.Change = uint32(0)
50
+
51
+	return msg
52
+}
53
+
54
+func (msg *IfInfomsg) ToWireFormat() []byte {
55
+	native := nativeEndian()
56
+
57
+	len := syscall.SizeofIfInfomsg
58
+	b := make([]byte, len)
59
+	b[0] = msg.Family
60
+	b[1] = 0
61
+	native.PutUint16(b[2:4], msg.Type)
62
+	native.PutUint32(b[4:8], uint32(msg.Index))
63
+	native.PutUint32(b[8:12], msg.Flags)
64
+	native.PutUint32(b[12:16], msg.Change)
65
+	return b
66
+}
67
+
68
+type IfAddrmsg struct {
69
+	syscall.IfAddrmsg
70
+}
71
+
72
+func newIfAddrmsg(family int) *IfAddrmsg {
73
+	msg := &IfAddrmsg{}
74
+	msg.Family = uint8(family)
75
+	msg.Prefixlen = uint8(0)
76
+	msg.Flags = uint8(0)
77
+	msg.Scope = uint8(0)
78
+	msg.Index = uint32(0)
79
+
80
+	return msg
81
+}
82
+
83
+func (msg *IfAddrmsg) ToWireFormat() []byte {
84
+	native := nativeEndian()
85
+
86
+	len := syscall.SizeofIfAddrmsg
87
+	b := make([]byte, len)
88
+	b[0] = msg.Family
89
+	b[1] = msg.Prefixlen
90
+	b[2] = msg.Flags
91
+	b[3] = msg.Scope
92
+	native.PutUint32(b[4:8], msg.Index)
93
+	return b
94
+}
95
+
96
+type RtMsg struct {
97
+	syscall.RtMsg
98
+}
99
+
100
+func newRtMsg(family int) *RtMsg {
101
+	msg := &RtMsg{}
102
+	msg.Family = uint8(family)
103
+	msg.Table = syscall.RT_TABLE_MAIN
104
+	msg.Scope = syscall.RT_SCOPE_UNIVERSE
105
+	msg.Protocol = syscall.RTPROT_BOOT
106
+	msg.Type = syscall.RTN_UNICAST
107
+
108
+	return msg
109
+}
110
+
111
+func (msg *RtMsg) ToWireFormat() []byte {
112
+	native := nativeEndian()
113
+
114
+	len := syscall.SizeofRtMsg
115
+	b := make([]byte, len)
116
+	b[0] = msg.Family
117
+	b[1] = msg.Dst_len
118
+	b[2] = msg.Src_len
119
+	b[3] = msg.Tos
120
+	b[4] = msg.Table
121
+	b[5] = msg.Protocol
122
+	b[6] = msg.Scope
123
+	b[7] = msg.Type
124
+	native.PutUint32(b[8:12], msg.Flags)
125
+	return b
126
+}
127
+
128
+func rtaAlignOf(attrlen int) int {
129
+	return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
130
+}
131
+
132
+type RtAttr struct {
133
+	syscall.RtAttr
134
+	Data []byte
135
+}
136
+
137
+func newRtAttr(attrType int, data []byte) *RtAttr {
138
+	attr := &RtAttr{}
139
+	attr.Type = uint16(attrType)
140
+	attr.Data = data
141
+
142
+	return attr
143
+}
144
+
145
+func (attr *RtAttr) ToWireFormat() []byte {
146
+	native := nativeEndian()
147
+
148
+	len := syscall.SizeofRtAttr + len(attr.Data)
149
+	b := make([]byte, rtaAlignOf(len))
150
+	native.PutUint16(b[0:2], uint16(len))
151
+	native.PutUint16(b[2:4], attr.Type)
152
+	for i, d := range attr.Data {
153
+		b[4+i] = d
154
+	}
155
+
156
+	return b
157
+}
158
+
159
+type NetlinkRequest struct {
160
+	syscall.NlMsghdr
161
+	Data []NetlinkRequestData
162
+}
163
+
164
+func (rr *NetlinkRequest) ToWireFormat() []byte {
165
+	native := nativeEndian()
166
+
167
+	length := rr.Len
168
+	dataBytes := make([][]byte, len(rr.Data))
169
+	for i, data := range rr.Data {
170
+		dataBytes[i] = data.ToWireFormat()
171
+		length = length + uint32(len(dataBytes[i]))
172
+	}
173
+	b := make([]byte, length)
174
+	native.PutUint32(b[0:4], length)
175
+	native.PutUint16(b[4:6], rr.Type)
176
+	native.PutUint16(b[6:8], rr.Flags)
177
+	native.PutUint32(b[8:12], rr.Seq)
178
+	native.PutUint32(b[12:16], rr.Pid)
179
+
180
+	i := 16
181
+	for _, data := range dataBytes {
182
+		for _, dataByte := range data {
183
+			b[i] = dataByte
184
+			i = i + 1
185
+		}
186
+	}
187
+	return b
188
+}
189
+
190
+func (rr *NetlinkRequest) AddData(data NetlinkRequestData) {
191
+	rr.Data = append(rr.Data, data)
192
+}
193
+
194
+func newNetlinkRequest(proto, flags int) *NetlinkRequest {
195
+	rr := &NetlinkRequest{}
196
+	rr.Len = uint32(syscall.NLMSG_HDRLEN)
197
+	rr.Type = uint16(proto)
198
+	rr.Flags = syscall.NLM_F_REQUEST | uint16(flags)
199
+	rr.Seq = uint32(getSeq())
200
+	return rr
201
+}
202
+
203
+type NetlinkSocket struct {
204
+	fd  int
205
+	lsa syscall.SockaddrNetlink
206
+}
207
+
208
+func getNetlinkSocket() (*NetlinkSocket, error) {
209
+	fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE)
210
+	if err != nil {
211
+		return nil, err
212
+	}
213
+	s := &NetlinkSocket{
214
+		fd: fd,
215
+	}
216
+	s.lsa.Family = syscall.AF_NETLINK
217
+	if err := syscall.Bind(fd, &s.lsa); err != nil {
218
+		syscall.Close(fd)
219
+		return nil, err
220
+	}
221
+
222
+	return s, nil
223
+}
224
+
225
+func (s *NetlinkSocket) Close() {
226
+	syscall.Close(s.fd)
227
+}
228
+
229
+func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
230
+	if err := syscall.Sendto(s.fd, request.ToWireFormat(), 0, &s.lsa); err != nil {
231
+		return err
232
+	}
233
+	return nil
234
+}
235
+
236
+func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) {
237
+	rb := make([]byte, syscall.Getpagesize())
238
+	nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
239
+	if err != nil {
240
+		return nil, err
241
+	}
242
+	if nr < syscall.NLMSG_HDRLEN {
243
+		return nil, fmt.Errorf("Got short response from netlink")
244
+	}
245
+	rb = rb[:nr]
246
+	return syscall.ParseNetlinkMessage(rb)
247
+}
248
+
249
+func (s *NetlinkSocket) GetPid() (uint32, error) {
250
+	lsa, err := syscall.Getsockname(s.fd)
251
+	if err != nil {
252
+		return 0, err
253
+	}
254
+	switch v := lsa.(type) {
255
+	case *syscall.SockaddrNetlink:
256
+		return v.Pid, nil
257
+	}
258
+	return 0, fmt.Errorf("Wrong socket type")
259
+}
260
+
261
+func (s *NetlinkSocket) HandleAck(seq uint32) error {
262
+	native := nativeEndian()
263
+
264
+	pid, err := s.GetPid()
265
+	if err != nil {
266
+		return err
267
+	}
268
+
269
+done:
270
+	for {
271
+		msgs, err := s.Recieve()
272
+		if err != nil {
273
+			return err
274
+		}
275
+		for _, m := range msgs {
276
+			if m.Header.Seq != seq {
277
+				return fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq)
278
+			}
279
+			if m.Header.Pid != pid {
280
+				return fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
281
+			}
282
+			if m.Header.Type == syscall.NLMSG_DONE {
283
+				break done
284
+			}
285
+			if m.Header.Type == syscall.NLMSG_ERROR {
286
+				error := int32(native.Uint32(m.Data[0:4]))
287
+				if error == 0 {
288
+					break done
289
+				}
290
+				return syscall.Errno(-error)
291
+			}
292
+		}
293
+	}
294
+
295
+	return nil
296
+}
297
+
298
+// Add a new default gateway. Identical to:
299
+// ip route add default via $ip
300
+func AddDefaultGw(ip net.IP) error {
301
+	s, err := getNetlinkSocket()
302
+	if err != nil {
303
+		return err
304
+	}
305
+	defer s.Close()
306
+
307
+	family := getIpFamily(ip)
308
+
309
+	wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
310
+
311
+	msg := newRtMsg(family)
312
+	wb.AddData(msg)
313
+
314
+	var ipData []byte
315
+	if family == syscall.AF_INET {
316
+		ipData = ip.To4()
317
+	} else {
318
+		ipData = ip.To16()
319
+	}
320
+
321
+	gateway := newRtAttr(syscall.RTA_GATEWAY, ipData)
322
+
323
+	wb.AddData(gateway)
324
+
325
+	if err := s.Send(wb); err != nil {
326
+		return err
327
+	}
328
+
329
+	return s.HandleAck(wb.Seq)
330
+
331
+}
332
+
333
+// Bring up a particular network interface
334
+func NetworkLinkUp(iface *net.Interface) error {
335
+	s, err := getNetlinkSocket()
336
+	if err != nil {
337
+		return err
338
+	}
339
+	defer s.Close()
340
+
341
+	wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
342
+
343
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
344
+	msg.Change = syscall.IFF_UP
345
+	msg.Flags = syscall.IFF_UP
346
+	msg.Index = int32(iface.Index)
347
+	wb.AddData(msg)
348
+
349
+	if err := s.Send(wb); err != nil {
350
+		return err
351
+	}
352
+
353
+	return s.HandleAck(wb.Seq)
354
+}
355
+
356
+// Add an Ip address to an interface. This is identical to:
357
+// ip addr add $ip/$ipNet dev $iface
358
+func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
359
+	s, err := getNetlinkSocket()
360
+	if err != nil {
361
+		return err
362
+	}
363
+	defer s.Close()
364
+
365
+	family := getIpFamily(ip)
366
+
367
+	wb := newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
368
+
369
+	msg := newIfAddrmsg(family)
370
+	msg.Index = uint32(iface.Index)
371
+	prefixLen, _ := ipNet.Mask.Size()
372
+	msg.Prefixlen = uint8(prefixLen)
373
+	wb.AddData(msg)
374
+
375
+	var ipData []byte
376
+	if family == syscall.AF_INET {
377
+		ipData = ip.To4()
378
+	} else {
379
+		ipData = ip.To16()
380
+	}
381
+
382
+	localData := newRtAttr(syscall.IFA_LOCAL, ipData)
383
+	wb.AddData(localData)
384
+
385
+	addrData := newRtAttr(syscall.IFA_ADDRESS, ipData)
386
+	wb.AddData(addrData)
387
+
388
+	if err := s.Send(wb); err != nil {
389
+		return err
390
+	}
391
+
392
+	return s.HandleAck(wb.Seq)
393
+}
394
+
395
+func zeroTerminated(s string) []byte {
396
+	bytes := make([]byte, len(s)+1)
397
+	for i := 0; i < len(s); i++ {
398
+		bytes[i] = s[i]
399
+	}
400
+	bytes[len(s)] = 0
401
+	return bytes
402
+}
403
+
404
+func nonZeroTerminated(s string) []byte {
405
+	bytes := make([]byte, len(s))
406
+	for i := 0; i < len(s); i++ {
407
+		bytes[i] = s[i]
408
+	}
409
+	return bytes
410
+}
411
+
412
+// Add a new network link of a specified type. This is identical to
413
+// running: ip add link $name type $linkType
414
+func NetworkLinkAdd(name string, linkType string) error {
415
+	s, err := getNetlinkSocket()
416
+	if err != nil {
417
+		return err
418
+	}
419
+	defer s.Close()
420
+
421
+	wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
422
+
423
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
424
+	wb.AddData(msg)
425
+
426
+	nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
427
+	wb.AddData(nameData)
428
+
429
+	IFLA_INFO_KIND := 1
430
+
431
+	kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType))
432
+
433
+	infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat())
434
+	wb.AddData(infoData)
435
+
436
+	if err := s.Send(wb); err != nil {
437
+		return err
438
+	}
439
+
440
+	return s.HandleAck(wb.Seq)
441
+}
442
+
443
+// Returns an array of IPNet for all the currently routed subnets on ipv4
444
+// This is similar to the first column of "ip route" output
445
+func NetworkGetRoutes() ([]*net.IPNet, error) {
446
+	native := nativeEndian()
447
+
448
+	s, err := getNetlinkSocket()
449
+	if err != nil {
450
+		return nil, err
451
+	}
452
+	defer s.Close()
453
+
454
+	wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
455
+
456
+	msg := newIfInfomsg(syscall.AF_UNSPEC)
457
+	wb.AddData(msg)
458
+
459
+	if err := s.Send(wb); err != nil {
460
+		return nil, err
461
+	}
462
+
463
+	pid, err := s.GetPid()
464
+	if err != nil {
465
+		return nil, err
466
+	}
467
+
468
+	res := make([]*net.IPNet, 0)
469
+
470
+done:
471
+	for {
472
+		msgs, err := s.Recieve()
473
+		if err != nil {
474
+			return nil, err
475
+		}
476
+		for _, m := range msgs {
477
+			if m.Header.Seq != wb.Seq {
478
+				return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
479
+			}
480
+			if m.Header.Pid != pid {
481
+				return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
482
+			}
483
+			if m.Header.Type == syscall.NLMSG_DONE {
484
+				break done
485
+			}
486
+			if m.Header.Type == syscall.NLMSG_ERROR {
487
+				error := int32(native.Uint32(m.Data[0:4]))
488
+				if error == 0 {
489
+					break done
490
+				}
491
+				return nil, syscall.Errno(-error)
492
+			}
493
+			if m.Header.Type != syscall.RTM_NEWROUTE {
494
+				continue
495
+			}
496
+
497
+			var iface *net.Interface = nil
498
+			var ipNet *net.IPNet = nil
499
+
500
+			msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0]))
501
+
502
+			if msg.Flags&syscall.RTM_F_CLONED != 0 {
503
+				// Ignore cloned routes
504
+				continue
505
+			}
506
+
507
+			if msg.Table != syscall.RT_TABLE_MAIN {
508
+				// Ignore non-main tables
509
+				continue
510
+			}
511
+
512
+			if msg.Family != syscall.AF_INET {
513
+				// Ignore non-ipv4 routes
514
+				continue
515
+			}
516
+
517
+			if msg.Dst_len == 0 {
518
+				// Ignore default routes
519
+				continue
520
+			}
521
+
522
+			attrs, err := syscall.ParseNetlinkRouteAttr(&m)
523
+			if err != nil {
524
+				return nil, err
525
+			}
526
+			for _, attr := range attrs {
527
+				switch attr.Attr.Type {
528
+				case syscall.RTA_DST:
529
+					ip := attr.Value
530
+					ipNet = &net.IPNet{
531
+						IP:   ip,
532
+						Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)),
533
+					}
534
+				case syscall.RTA_OIF:
535
+					index := int(native.Uint32(attr.Value[0:4]))
536
+					iface, _ = net.InterfaceByIndex(index)
537
+					_ = iface
538
+				}
539
+			}
540
+			if ipNet != nil {
541
+				res = append(res, ipNet)
542
+			}
543
+		}
544
+	}
545
+
546
+	return res, nil
547
+}
... ...
@@ -76,6 +76,21 @@ func checkRouteOverlaps(networks []*net.IPNet, dockerNetwork *net.IPNet) error {
76 76
 	return nil
77 77
 }
78 78
 
79
+func checkNameserverOverlaps(nameservers []string, dockerNetwork *net.IPNet) error {
80
+	if len(nameservers) > 0 {
81
+		for _, ns := range nameservers {
82
+			_, nsNetwork, err := net.ParseCIDR(ns)
83
+			if err != nil {
84
+				return err
85
+			}
86
+			if networkOverlaps(dockerNetwork, nsNetwork) {
87
+				return fmt.Errorf("%s overlaps nameserver %s", dockerNetwork, nsNetwork)
88
+			}
89
+		}
90
+	}
91
+	return nil
92
+}
93
+
79 94
 // CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`,
80 95
 // and attempts to configure it with an address which doesn't conflict with any other interface on the host.
81 96
 // If it can't find an address which doesn't conflict, it will return an error.
... ...
@@ -100,6 +115,16 @@ func CreateBridgeIface(config *DaemonConfig) error {
100 100
 		"192.168.44.1/24",
101 101
 	}
102 102
 
103
+	nameservers := []string{}
104
+	resolvConf, _ := utils.GetResolvConf()
105
+	// we don't check for an error here, because we don't really care
106
+	// if we can't read /etc/resolv.conf. So instead we skip the append
107
+	// if resolvConf is nil. It either doesn't exist, or we can't read it
108
+	// for some reason.
109
+	if resolvConf != nil {
110
+		nameservers = append(nameservers, utils.GetNameserversAsCIDR(resolvConf)...)
111
+	}
112
+
103 113
 	var ifaceAddr string
104 114
 	for _, addr := range addrs {
105 115
 		_, dockerNetwork, err := net.ParseCIDR(addr)
... ...
@@ -111,8 +136,10 @@ func CreateBridgeIface(config *DaemonConfig) error {
111 111
 			return err
112 112
 		}
113 113
 		if err := checkRouteOverlaps(routes, dockerNetwork); err == nil {
114
-			ifaceAddr = addr
115
-			break
114
+			if err := checkNameserverOverlaps(nameservers, dockerNetwork); err == nil {
115
+				ifaceAddr = addr
116
+				break
117
+			}
116 118
 		} else {
117 119
 			utils.Debugf("%s: %s", addr, err)
118 120
 		}
... ...
@@ -295,3 +295,19 @@ func TestCheckRouteOverlaps(t *testing.T) {
295 295
 		t.Fatalf("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't")
296 296
 	}
297 297
 }
298
+
299
+func TestCheckNameserverOverlaps(t *testing.T) {
300
+	nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"}
301
+
302
+	_, netX, _ := net.ParseCIDR("10.0.2.3/32")
303
+
304
+	if err := checkNameserverOverlaps(nameservers, netX); err == nil {
305
+		t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX)
306
+	}
307
+
308
+	_, netX, _ = net.ParseCIDR("192.168.102.2/32")
309
+
310
+	if err := checkNameserverOverlaps(nameservers, netX); err != nil {
311
+		t.Fatalf("%s should not overlap %v but it does", netX, nameservers)
312
+	}
313
+}
... ...
@@ -615,10 +615,18 @@ func (r *Registry) GetAuthConfig(withPasswd bool) *auth.AuthConfig {
615 615
 	}
616 616
 }
617 617
 
618
+type SearchResult struct {
619
+	StarCount   int    `json:"star_count"`
620
+	IsOfficial  bool   `json:"is_official"`
621
+	Name        string `json:"name"`
622
+	IsTrusted   bool   `json:"is_trusted"`
623
+	Description string `json:"description"`
624
+}
625
+
618 626
 type SearchResults struct {
619
-	Query      string              `json:"query"`
620
-	NumResults int                 `json:"num_results"`
621
-	Results    []map[string]string `json:"results"`
627
+	Query      string         `json:"query"`
628
+	NumResults int            `json:"num_results"`
629
+	Results    []SearchResult `json:"results"`
622 630
 }
623 631
 
624 632
 type RepositoryData struct {
... ...
@@ -24,6 +24,7 @@ type Capabilities struct {
24 24
 	MemoryLimit            bool
25 25
 	SwapLimit              bool
26 26
 	IPv4ForwardingDisabled bool
27
+	AppArmor               bool
27 28
 }
28 29
 
29 30
 type Runtime struct {
... ...
@@ -112,6 +113,9 @@ func (runtime *Runtime) Register(container *Container) error {
112 112
 	if err := validateID(container.ID); err != nil {
113 113
 		return err
114 114
 	}
115
+	if err := runtime.ensureName(container); err != nil {
116
+		return err
117
+	}
115 118
 
116 119
 	// init the wait lock
117 120
 	container.waitLock = make(chan struct{})
... ...
@@ -149,8 +153,7 @@ func (runtime *Runtime) Register(container *Container) error {
149 149
 				utils.Debugf("Restarting")
150 150
 				container.State.Ghost = false
151 151
 				container.State.setStopped(0)
152
-				hostConfig, _ := container.ReadHostConfig()
153
-				if err := container.Start(hostConfig); err != nil {
152
+				if err := container.Start(); err != nil {
154 153
 					return err
155 154
 				}
156 155
 				nomonitor = true
... ...
@@ -169,9 +172,27 @@ func (runtime *Runtime) Register(container *Container) error {
169 169
 	if !container.State.Running {
170 170
 		close(container.waitLock)
171 171
 	} else if !nomonitor {
172
-		hostConfig, _ := container.ReadHostConfig()
173
-		container.allocateNetwork(hostConfig)
174
-		go container.monitor(hostConfig)
172
+		go container.monitor()
173
+	}
174
+	return nil
175
+}
176
+
177
+func (runtime *Runtime) ensureName(container *Container) error {
178
+	if container.Name == "" {
179
+		name, err := generateRandomName(runtime)
180
+		if err != nil {
181
+			name = container.ShortID()
182
+		}
183
+		container.Name = name
184
+
185
+		if err := container.ToDisk(); err != nil {
186
+			utils.Debugf("Error saving container name %s", err)
187
+		}
188
+		if !runtime.containerGraph.Exists(name) {
189
+			if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
190
+				utils.Debugf("Setting default id - %s", err)
191
+			}
192
+		}
175 193
 	}
176 194
 	return nil
177 195
 }
... ...
@@ -265,7 +286,10 @@ func (runtime *Runtime) restore() error {
265 265
 	// Any containers that are left over do not exist in the graph
266 266
 	for _, container := range containers {
267 267
 		// Try to set the default name for a container if it exists prior to links
268
-		name := generateRandomName(runtime)
268
+		name, err := generateRandomName(runtime)
269
+		if err != nil {
270
+			container.Name = container.ShortID()
271
+		}
269 272
 		container.Name = name
270 273
 
271 274
 		if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
... ...
@@ -307,6 +331,15 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) {
307 307
 	if runtime.capabilities.IPv4ForwardingDisabled && !quiet {
308 308
 		log.Printf("WARNING: IPv4 forwarding is disabled.")
309 309
 	}
310
+
311
+	// Check if AppArmor seems to be enabled on this system.
312
+	if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
313
+		utils.Debugf("/sys/kernel/security/apparmor not found; assuming AppArmor is not enabled.")
314
+		runtime.capabilities.AppArmor = false
315
+	} else {
316
+		utils.Debugf("/sys/kernel/security/apparmor found; assuming AppArmor is enabled.")
317
+		runtime.capabilities.AppArmor = true
318
+	}
310 319
 }
311 320
 
312 321
 // Create creates a new container from the given configuration with a given name.
... ...
@@ -356,7 +389,10 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
356 356
 	id := GenerateID()
357 357
 
358 358
 	if name == "" {
359
-		name = generateRandomName(runtime)
359
+		name, err = generateRandomName(runtime)
360
+		if err != nil {
361
+			name = utils.TruncateID(id)
362
+		}
360 363
 	}
361 364
 	if name[0] != '/' {
362 365
 		name = "/" + name
... ...
@@ -394,6 +430,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
394 394
 		Path:            entrypoint,
395 395
 		Args:            args, //FIXME: de-duplicate from config
396 396
 		Config:          config,
397
+		hostConfig:      &HostConfig{},
397 398
 		Image:           img.ID, // Always use the resolved image id
398 399
 		NetworkSettings: &NetworkSettings{},
399 400
 		// FIXME: do we need to store this in the container?
... ...
@@ -504,15 +541,22 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a
504 504
 	return img, nil
505 505
 }
506 506
 
507
-func (runtime *Runtime) getFullName(name string) string {
507
+func (runtime *Runtime) getFullName(name string) (string, error) {
508
+	if name == "" {
509
+		return "", fmt.Errorf("Container name cannot be empty")
510
+	}
508 511
 	if name[0] != '/' {
509 512
 		name = "/" + name
510 513
 	}
511
-	return name
514
+	return name, nil
512 515
 }
513 516
 
514 517
 func (runtime *Runtime) GetByName(name string) (*Container, error) {
515
-	entity := runtime.containerGraph.Get(runtime.getFullName(name))
518
+	fullName, err := runtime.getFullName(name)
519
+	if err != nil {
520
+		return nil, err
521
+	}
522
+	entity := runtime.containerGraph.Get(fullName)
516 523
 	if entity == nil {
517 524
 		return nil, fmt.Errorf("Could not find entity for %s", name)
518 525
 	}
... ...
@@ -524,10 +568,13 @@ func (runtime *Runtime) GetByName(name string) (*Container, error) {
524 524
 }
525 525
 
526 526
 func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
527
-	name = runtime.getFullName(name)
527
+	name, err := runtime.getFullName(name)
528
+	if err != nil {
529
+		return nil, err
530
+	}
528 531
 	children := make(map[string]*Container)
529 532
 
530
-	err := runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error {
533
+	err = runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error {
531 534
 		c := runtime.Get(e.ID())
532 535
 		if c == nil {
533 536
 			return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
... ...
@@ -557,34 +604,29 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) {
557 557
 	if err != nil {
558 558
 		return nil, err
559 559
 	}
560
-
561
-	if k, err := utils.GetKernelVersion(); err != nil {
562
-		log.Printf("WARNING: %s\n", err)
563
-	} else {
564
-		if utils.CompareKernelVersion(k, &utils.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 {
565
-			log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String())
566
-		}
567
-	}
568 560
 	runtime.UpdateCapabilities(false)
569 561
 	return runtime, nil
570 562
 }
571 563
 
572 564
 func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
573
-	runtimeRepo := path.Join(config.GraphPath, "containers")
565
+	runtimeRepo := path.Join(config.Root, "containers")
574 566
 
575 567
 	if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
576 568
 		return nil, err
577 569
 	}
578 570
 
579
-	g, err := NewGraph(path.Join(config.GraphPath, "graph"))
571
+	if err := linkLxcStart(config.Root); err != nil {
572
+		return nil, err
573
+	}
574
+	g, err := NewGraph(path.Join(config.Root, "graph"))
580 575
 	if err != nil {
581 576
 		return nil, err
582 577
 	}
583
-	volumes, err := NewGraph(path.Join(config.GraphPath, "volumes"))
578
+	volumes, err := NewGraph(path.Join(config.Root, "volumes"))
584 579
 	if err != nil {
585 580
 		return nil, err
586 581
 	}
587
-	repositories, err := NewTagStore(path.Join(config.GraphPath, "repositories"), g)
582
+	repositories, err := NewTagStore(path.Join(config.Root, "repositories"), g)
588 583
 	if err != nil {
589 584
 		return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
590 585
 	}
... ...
@@ -596,7 +638,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
596 596
 		return nil, err
597 597
 	}
598 598
 
599
-	gographPath := path.Join(config.GraphPath, "linkgraph.db")
599
+	gographPath := path.Join(config.Root, "linkgraph.db")
600 600
 	initDatabase := false
601 601
 	if _, err := os.Stat(gographPath); err != nil {
602 602
 		if os.IsNotExist(err) {
... ...
@@ -638,6 +680,23 @@ func (runtime *Runtime) Close() error {
638 638
 	return runtime.containerGraph.Close()
639 639
 }
640 640
 
641
+func linkLxcStart(root string) error {
642
+	sourcePath, err := exec.LookPath("lxc-start")
643
+	if err != nil {
644
+		return err
645
+	}
646
+	targetPath := path.Join(root, "lxc-start-unconfined")
647
+
648
+	if _, err := os.Stat(targetPath); err != nil && !os.IsNotExist(err) {
649
+		return err
650
+	} else if err == nil {
651
+		if err := os.Remove(targetPath); err != nil {
652
+			return err
653
+		}
654
+	}
655
+	return os.Symlink(sourcePath, targetPath)
656
+}
657
+
641 658
 // History is a convenience type for storing a list of containers,
642 659
 // ordered by creation date.
643 660
 type History []*Container
... ...
@@ -46,8 +46,8 @@ func nuke(runtime *Runtime) error {
46 46
 	wg.Wait()
47 47
 	runtime.Close()
48 48
 
49
-	os.Remove(filepath.Join(runtime.config.GraphPath, "linkgraph.db"))
50
-	return os.RemoveAll(runtime.config.GraphPath)
49
+	os.Remove(filepath.Join(runtime.config.Root, "linkgraph.db"))
50
+	return os.RemoveAll(runtime.config.Root)
51 51
 }
52 52
 
53 53
 func cleanup(runtime *Runtime) error {
... ...
@@ -119,7 +119,7 @@ func init() {
119 119
 
120 120
 func setupBaseImage() {
121 121
 	config := &DaemonConfig{
122
-		GraphPath:   unitTestStoreBase,
122
+		Root:        unitTestStoreBase,
123 123
 		AutoRestart: false,
124 124
 		BridgeIface: unitTestNetworkBridge,
125 125
 	}
... ...
@@ -325,13 +325,13 @@ func TestGet(t *testing.T) {
325 325
 	runtime := mkRuntime(t)
326 326
 	defer nuke(runtime)
327 327
 
328
-	container1, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
328
+	container1, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
329 329
 	defer runtime.Destroy(container1)
330 330
 
331
-	container2, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
331
+	container2, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
332 332
 	defer runtime.Destroy(container2)
333 333
 
334
-	container3, _, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
334
+	container3, _ := mkContainer(runtime, []string{"_", "ls", "-al"}, t)
335 335
 	defer runtime.Destroy(container3)
336 336
 
337 337
 	if runtime.Get(container1.ID) != container1 {
... ...
@@ -390,13 +390,13 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container,
390 390
 		t.Logf("Port %v already in use, trying another one", strPort)
391 391
 	}
392 392
 
393
-	hostConfig := &HostConfig{
393
+	container.hostConfig = &HostConfig{
394 394
 		PortBindings: make(map[Port][]PortBinding),
395 395
 	}
396
-	hostConfig.PortBindings[p] = []PortBinding{
396
+	container.hostConfig.PortBindings[p] = []PortBinding{
397 397
 		{},
398 398
 	}
399
-	if err := container.Start(hostConfig); err != nil {
399
+	if err := container.Start(); err != nil {
400 400
 		nuke(runtime)
401 401
 		t.Fatal(err)
402 402
 	}
... ...
@@ -503,16 +503,15 @@ func TestRestore(t *testing.T) {
503 503
 	runtime1 := mkRuntime(t)
504 504
 	defer nuke(runtime1)
505 505
 	// Create a container with one instance of docker
506
-	container1, _, _ := mkContainer(runtime1, []string{"_", "ls", "-al"}, t)
506
+	container1, _ := mkContainer(runtime1, []string{"_", "ls", "-al"}, t)
507 507
 	defer runtime1.Destroy(container1)
508 508
 
509 509
 	// Create a second container meant to be killed
510
-	container2, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
510
+	container2, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
511 511
 	defer runtime1.Destroy(container2)
512 512
 
513 513
 	// Start the container non blocking
514
-	hostConfig := &HostConfig{}
515
-	if err := container2.Start(hostConfig); err != nil {
514
+	if err := container2.Start(); err != nil {
516 515
 		t.Fatal(err)
517 516
 	}
518 517
 
... ...
@@ -575,25 +574,23 @@ func TestReloadContainerLinks(t *testing.T) {
575 575
 	runtime1 := mkRuntime(t)
576 576
 	defer nuke(runtime1)
577 577
 	// Create a container with one instance of docker
578
-	container1, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/sh"}, t)
578
+	container1, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/sh"}, t)
579 579
 	defer runtime1.Destroy(container1)
580 580
 
581 581
 	// Create a second container meant to be killed
582
-	container2, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
582
+	container2, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
583 583
 	defer runtime1.Destroy(container2)
584 584
 
585 585
 	// Start the container non blocking
586
-	hostConfig := &HostConfig{}
587
-	if err := container2.Start(hostConfig); err != nil {
586
+	if err := container2.Start(); err != nil {
588 587
 		t.Fatal(err)
589 588
 	}
590
-	h1 := &HostConfig{}
591 589
 	// Add a link to container 2
592
-	h1.Links = []string{"/" + container2.ID + ":first"}
590
+	container1.hostConfig.Links = []string{"/" + container2.ID + ":first"}
593 591
 	if err := runtime1.RegisterLink(container1, container2, "first"); err != nil {
594 592
 		t.Fatal(err)
595 593
 	}
596
-	if err := container1.Start(h1); err != nil {
594
+	if err := container1.Start(); err != nil {
597 595
 		t.Fatal(err)
598 596
 	}
599 597
 
... ...
@@ -623,7 +620,6 @@ func TestReloadContainerLinks(t *testing.T) {
623 623
 	runningCount := 0
624 624
 	for _, c := range runtime2.List() {
625 625
 		if c.State.Running {
626
-			t.Logf("Running container found: %v (%v)", c.ID, c.Path)
627 626
 			runningCount++
628 627
 		}
629 628
 	}
... ...
@@ -638,7 +634,6 @@ func TestReloadContainerLinks(t *testing.T) {
638 638
 		t.Fatalf("Container 2 %s should be registered first in the runtime", container2.ID)
639 639
 	}
640 640
 
641
-	t.Logf("Number of links: %d", runtime2.containerGraph.Refs("0"))
642 641
 	// Verify that the link is still registered in the runtime
643 642
 	entity := runtime2.containerGraph.Get(container1.Name)
644 643
 	if entity == nil {
... ...
@@ -833,3 +828,19 @@ func TestGetAllChildren(t *testing.T) {
833 833
 		}
834 834
 	}
835 835
 }
836
+
837
+func TestGetFullName(t *testing.T) {
838
+	runtime := mkRuntime(t)
839
+	defer nuke(runtime)
840
+
841
+	name, err := runtime.getFullName("testing")
842
+	if err != nil {
843
+		t.Fatal(err)
844
+	}
845
+	if name != "/testing" {
846
+		t.Fatalf("Expected /testing got %s", name)
847
+	}
848
+	if _, err := runtime.getFullName(""); err == nil {
849
+		t.Fatal("Error should not be nil")
850
+	}
851
+}
... ...
@@ -5,7 +5,9 @@ import (
5 5
 	"encoding/json"
6 6
 	"errors"
7 7
 	"fmt"
8
+	"github.com/dotcloud/docker/archive"
8 9
 	"github.com/dotcloud/docker/auth"
10
+	"github.com/dotcloud/docker/engine"
9 11
 	"github.com/dotcloud/docker/gograph"
10 12
 	"github.com/dotcloud/docker/registry"
11 13
 	"github.com/dotcloud/docker/utils"
... ...
@@ -16,11 +18,13 @@ import (
16 16
 	"net/url"
17 17
 	"os"
18 18
 	"os/exec"
19
+	"os/signal"
19 20
 	"path"
20 21
 	"path/filepath"
21 22
 	"runtime"
22 23
 	"strings"
23 24
 	"sync"
25
+	"syscall"
24 26
 	"time"
25 27
 )
26 28
 
... ...
@@ -28,6 +32,70 @@ func (srv *Server) Close() error {
28 28
 	return srv.runtime.Close()
29 29
 }
30 30
 
31
+func init() {
32
+	engine.Register("serveapi", JobServeApi)
33
+}
34
+
35
+func JobServeApi(job *engine.Job) string {
36
+	srv, err := NewServer(ConfigFromJob(job))
37
+	if err != nil {
38
+		return err.Error()
39
+	}
40
+	defer srv.Close()
41
+	if err := srv.Daemon(); err != nil {
42
+		return err.Error()
43
+	}
44
+	return "0"
45
+}
46
+
47
+// Daemon runs the remote api server `srv` as a daemon,
48
+// Only one api server can run at the same time - this is enforced by a pidfile.
49
+// The signals SIGINT, SIGKILL and SIGTERM are intercepted for cleanup.
50
+func (srv *Server) Daemon() error {
51
+	if err := utils.CreatePidFile(srv.runtime.config.Pidfile); err != nil {
52
+		log.Fatal(err)
53
+	}
54
+	defer utils.RemovePidFile(srv.runtime.config.Pidfile)
55
+
56
+	c := make(chan os.Signal, 1)
57
+	signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM))
58
+	go func() {
59
+		sig := <-c
60
+		log.Printf("Received signal '%v', exiting\n", sig)
61
+		utils.RemovePidFile(srv.runtime.config.Pidfile)
62
+		srv.Close()
63
+		os.Exit(0)
64
+	}()
65
+
66
+	protoAddrs := srv.runtime.config.ProtoAddresses
67
+	chErrors := make(chan error, len(protoAddrs))
68
+	for _, protoAddr := range protoAddrs {
69
+		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
70
+		switch protoAddrParts[0] {
71
+		case "unix":
72
+			if err := syscall.Unlink(protoAddrParts[1]); err != nil && !os.IsNotExist(err) {
73
+				log.Fatal(err)
74
+			}
75
+		case "tcp":
76
+			if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") {
77
+				log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
78
+			}
79
+		default:
80
+			return fmt.Errorf("Invalid protocol format.")
81
+		}
82
+		go func() {
83
+			chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], srv, true)
84
+		}()
85
+	}
86
+	for i := 0; i < len(protoAddrs); i += 1 {
87
+		err := <-chErrors
88
+		if err != nil {
89
+			return err
90
+		}
91
+	}
92
+	return nil
93
+}
94
+
31 95
 func (srv *Server) DockerVersion() APIVersion {
32 96
 	return APIVersion{
33 97
 		Version:   VERSION,
... ...
@@ -118,8 +186,8 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
118 118
 	return fmt.Errorf("No such container: %s", name)
119 119
 }
120 120
 
121
-func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
122
-	r, err := registry.NewRegistry(srv.runtime.config.GraphPath, nil, srv.HTTPRequestFactory(nil))
121
+func (srv *Server) ImagesSearch(term string) ([]registry.SearchResult, error) {
122
+	r, err := registry.NewRegistry(srv.runtime.config.Root, nil, srv.HTTPRequestFactory(nil))
123 123
 	if err != nil {
124 124
 		return nil, err
125 125
 	}
... ...
@@ -127,15 +195,7 @@ func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
127 127
 	if err != nil {
128 128
 		return nil, err
129 129
 	}
130
-
131
-	var outs []APISearch
132
-	for _, repo := range results.Results {
133
-		var out APISearch
134
-		out.Description = repo["description"]
135
-		out.Name = repo["name"]
136
-		outs = append(outs, out)
137
-	}
138
-	return outs, nil
130
+	return results.Results, nil
139 131
 }
140 132
 
141 133
 func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.StreamFormatter) (string, error) {
... ...
@@ -320,10 +380,11 @@ func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
320 320
 	outs := []APIHistory{} //produce [] when empty instead of 'null'
321 321
 	err = image.WalkHistory(func(img *Image) error {
322 322
 		var out APIHistory
323
-		out.ID = srv.runtime.repositories.ImageName(img.ShortID())
323
+		out.ID = img.ID
324 324
 		out.Created = img.Created.Unix()
325 325
 		out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
326 326
 		out.Tags = lookupMap[img.ID]
327
+		out.Size = img.Size
327 328
 		outs = append(outs, out)
328 329
 		return nil
329 330
 	})
... ...
@@ -350,7 +411,11 @@ func (srv *Server) ContainerTop(name, ps_args string) (*APITop, error) {
350 350
 			}
351 351
 			// no scanner.Text because we skip container id
352 352
 			for scanner.Scan() {
353
-				words = append(words, scanner.Text())
353
+				if i != 0 && len(words) == len(procs.Titles) {
354
+					words[len(words)-1] = fmt.Sprintf("%s %s", words[len(words)-1], scanner.Text())
355
+				} else {
356
+					words = append(words, scanner.Text())
357
+				}
354 358
 			}
355 359
 			if i == 0 {
356 360
 				procs.Titles = words
... ...
@@ -663,7 +728,7 @@ func (srv *Server) poolRemove(kind, key string) error {
663 663
 }
664 664
 
665 665
 func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig, metaHeaders map[string][]string, parallel bool) error {
666
-	r, err := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders))
666
+	r, err := registry.NewRegistry(srv.runtime.config.Root, authConfig, srv.HTTPRequestFactory(metaHeaders))
667 667
 	if err != nil {
668 668
 		return err
669 669
 	}
... ...
@@ -836,7 +901,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID,
836 836
 		return "", err
837 837
 	}
838 838
 
839
-	layerData, err := srv.runtime.graph.TempLayerArchive(imgID, Uncompressed, sf, out)
839
+	layerData, err := srv.runtime.graph.TempLayerArchive(imgID, archive.Uncompressed, sf, out)
840 840
 	if err != nil {
841 841
 		return "", fmt.Errorf("Failed to generate layer archive: %s", err)
842 842
 	}
... ...
@@ -872,7 +937,7 @@ func (srv *Server) ImagePush(localName string, out io.Writer, sf *utils.StreamFo
872 872
 
873 873
 	out = utils.NewWriteFlusher(out)
874 874
 	img, err := srv.runtime.graph.Get(localName)
875
-	r, err2 := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders))
875
+	r, err2 := registry.NewRegistry(srv.runtime.config.Root, authConfig, srv.HTTPRequestFactory(metaHeaders))
876 876
 	if err2 != nil {
877 877
 		return err2
878 878
 	}
... ...
@@ -985,7 +1050,10 @@ func (srv *Server) ContainerDestroy(name string, removeVolume, removeLink bool)
985 985
 		if container == nil {
986 986
 			return fmt.Errorf("No such link: %s", name)
987 987
 		}
988
-		name = srv.runtime.getFullName(name)
988
+		name, err := srv.runtime.getFullName(name)
989
+		if err != nil {
990
+			return err
991
+		}
989 992
 		parent, n := path.Split(name)
990 993
 		if parent == "/" {
991 994
 			return fmt.Errorf("Conflict, cannot remove the default name of the container")
... ...
@@ -1238,7 +1306,7 @@ func (srv *Server) RegisterLinks(name string, hostConfig *HostConfig) error {
1238 1238
 		// After we load all the links into the runtime
1239 1239
 		// set them to nil on the hostconfig
1240 1240
 		hostConfig.Links = nil
1241
-		if err := container.SaveHostConfig(hostConfig); err != nil {
1241
+		if err := container.writeHostConfig(); err != nil {
1242 1242
 			return err
1243 1243
 		}
1244 1244
 	}
... ...
@@ -1248,11 +1316,33 @@ func (srv *Server) RegisterLinks(name string, hostConfig *HostConfig) error {
1248 1248
 func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
1249 1249
 	runtime := srv.runtime
1250 1250
 	container := runtime.Get(name)
1251
+
1252
+	if hostConfig != nil {
1253
+		for _, bind := range hostConfig.Binds {
1254
+			splitBind := strings.Split(bind, ":")
1255
+			source := splitBind[0]
1256
+
1257
+			// refuse to bind mount "/" to the container
1258
+			if source == "/" {
1259
+				return fmt.Errorf("Invalid bind mount '%s' : source can't be '/'", bind)
1260
+			}
1261
+
1262
+			// ensure the source exists on the host
1263
+			_, err := os.Stat(source)
1264
+			if err != nil && os.IsNotExist(err) {
1265
+				return fmt.Errorf("Invalid bind mount '%s' : source doesn't exist", bind)
1266
+			}
1267
+		}
1268
+	}
1269
+
1251 1270
 	if container == nil {
1252 1271
 		return fmt.Errorf("No such container: %s", name)
1253 1272
 	}
1254
-
1255
-	if err := container.Start(hostConfig); err != nil {
1273
+	if hostConfig != nil {
1274
+		container.hostConfig = hostConfig
1275
+		container.ToDisk()
1276
+	}
1277
+	if err := container.Start(); err != nil {
1256 1278
 		return fmt.Errorf("Cannot start container %s: %s", name, err)
1257 1279
 	}
1258 1280
 	srv.LogEvent("start", container.ShortID(), runtime.repositories.ImageName(container.Image))
... ...
@@ -1409,9 +1499,6 @@ func (srv *Server) ContainerCopy(name string, resource string, out io.Writer) er
1409 1409
 }
1410 1410
 
1411 1411
 func NewServer(config *DaemonConfig) (*Server, error) {
1412
-	if runtime.GOARCH != "amd64" {
1413
-		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
1414
-	}
1415 1412
 	runtime, err := NewRuntime(config)
1416 1413
 	if err != nil {
1417 1414
 		return nil, err
... ...
@@ -246,14 +246,14 @@ func TestContainerTop(t *testing.T) {
246 246
 
247 247
 	srv := &Server{runtime: runtime}
248 248
 
249
-	c, hostConfig, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t)
250
-	c, hostConfig, err := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t)
249
+	c, _ := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t)
250
+	c, err := mkContainer(runtime, []string{"_", "/bin/sh", "-c", "sleep 2"}, t)
251 251
 	if err != nil {
252 252
 		t.Fatal(err)
253 253
 	}
254 254
 
255 255
 	defer runtime.Destroy(c)
256
-	if err := c.Start(hostConfig); err != nil {
256
+	if err := c.Start(); err != nil {
257 257
 		t.Fatal(err)
258 258
 	}
259 259
 
... ...
@@ -1,10 +1,12 @@
1 1
 package sysinit
2 2
 
3 3
 import (
4
+	"encoding/json"
4 5
 	"flag"
5 6
 	"fmt"
6 7
 	"github.com/dotcloud/docker/netlink"
7 8
 	"github.com/dotcloud/docker/utils"
9
+	"io/ioutil"
8 10
 	"log"
9 11
 	"net"
10 12
 	"os"
... ...
@@ -69,9 +71,18 @@ func changeUser(u string) {
69 69
 }
70 70
 
71 71
 // Clear environment pollution introduced by lxc-start
72
-func cleanupEnv(env utils.ListOpts) {
72
+func cleanupEnv() {
73 73
 	os.Clearenv()
74
-	for _, kv := range env {
74
+	var lines []string
75
+	content, err := ioutil.ReadFile("/.dockerenv")
76
+	if err != nil {
77
+		log.Fatalf("Unable to load environment variables: %v", err)
78
+	}
79
+	err = json.Unmarshal(content, &lines)
80
+	if err != nil {
81
+		log.Fatalf("Unable to unmarshal environment variables: %v", err)
82
+	}
83
+	for _, kv := range lines {
75 84
 		parts := strings.SplitN(kv, "=", 2)
76 85
 		if len(parts) == 1 {
77 86
 			parts = append(parts, "")
... ...
@@ -104,12 +115,9 @@ func SysInit() {
104 104
 	var gw = flag.String("g", "", "gateway address")
105 105
 	var workdir = flag.String("w", "", "workdir")
106 106
 
107
-	var flEnv utils.ListOpts
108
-	flag.Var(&flEnv, "e", "Set environment variables")
109
-
110 107
 	flag.Parse()
111 108
 
112
-	cleanupEnv(flEnv)
109
+	cleanupEnv()
113 110
 	setupNetworking(*gw)
114 111
 	setupWorkingDirectory(*workdir)
115 112
 	changeUser(*u)
... ...
@@ -225,6 +225,12 @@ func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding,
225 225
 		if containerPort == "" {
226 226
 			return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
227 227
 		}
228
+		if _, err := strconv.ParseUint(containerPort, 10, 16); err != nil {
229
+			return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
230
+		}
231
+		if _, err := strconv.ParseUint(hostPort, 10, 16); hostPort != "" && err != nil {
232
+			return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
233
+		}
228 234
 
229 235
 		port := NewPort(proto, containerPort)
230 236
 		if _, exists := exposedPorts[port]; !exists {
... ...
@@ -304,10 +310,6 @@ func (c *checker) Exists(name string) bool {
304 304
 }
305 305
 
306 306
 // Generate a random and unique name
307
-func generateRandomName(runtime *Runtime) string {
308
-	n, err := namesgenerator.GenerateRandomName(&checker{runtime})
309
-	if err != nil {
310
-		panic(err)
311
-	}
312
-	return n
307
+func generateRandomName(runtime *Runtime) (string, error) {
308
+	return namesgenerator.GenerateRandomName(&checker{runtime})
313 309
 }
314 310
new file mode 100644
... ...
@@ -0,0 +1,36 @@
0
+package utils
1
+
2
+import (
3
+	"os"
4
+	"fmt"
5
+	"io/ioutil"
6
+	"log"
7
+	"strconv"
8
+)
9
+
10
+func CreatePidFile(pidfile string) error {
11
+	if pidString, err := ioutil.ReadFile(pidfile); err == nil {
12
+		pid, err := strconv.Atoi(string(pidString))
13
+		if err == nil {
14
+			if _, err := os.Stat(fmt.Sprintf("/proc/%d/", pid)); err == nil {
15
+				return fmt.Errorf("pid file found, ensure docker is not running or delete %s", pidfile)
16
+			}
17
+		}
18
+	}
19
+
20
+	file, err := os.Create(pidfile)
21
+	if err != nil {
22
+		return err
23
+	}
24
+
25
+	defer file.Close()
26
+
27
+	_, err = fmt.Fprintf(file, "%d", os.Getpid())
28
+	return err
29
+}
30
+
31
+func RemovePidFile(pidfile string) {
32
+	if err := os.Remove(pidfile); err != nil {
33
+		log.Printf("Error removing %s: %s", pidfile, err)
34
+	}
35
+}
0 36
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+package utils
1
+
2
+import (
3
+	"io"
4
+	"crypto/rand"
5
+	"encoding/hex"
6
+)
7
+
8
+func RandomString() string {
9
+	id := make([]byte, 32)
10
+	_, err := io.ReadFull(rand.Reader, id)
11
+	if err != nil {
12
+		panic(err) // This shouldn't happen
13
+	}
14
+	return hex.EncodeToString(id)
15
+}
... ...
@@ -16,6 +16,7 @@ import (
16 16
 	"os/exec"
17 17
 	"path/filepath"
18 18
 	"runtime"
19
+	"regexp"
19 20
 	"strconv"
20 21
 	"strings"
21 22
 	"sync"
... ...
@@ -695,6 +696,13 @@ func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
695 695
 	return n, err
696 696
 }
697 697
 
698
+// Flush the stream immediately.
699
+func (wf *WriteFlusher) Flush() {
700
+	wf.Lock()
701
+	defer wf.Unlock()
702
+	wf.flusher.Flush()
703
+}
704
+
698 705
 func NewWriteFlusher(w io.Writer) *WriteFlusher {
699 706
 	var flusher http.Flusher
700 707
 	if f, ok := w.(http.Flusher); ok {
... ...
@@ -896,6 +904,23 @@ func StripComments(input []byte, commentMarker []byte) []byte {
896 896
 	return output
897 897
 }
898 898
 
899
+// GetNameserversAsCIDR returns nameservers (if any) listed in 
900
+// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32")
901
+// This function's output is intended for net.ParseCIDR
902
+func GetNameserversAsCIDR(resolvConf []byte) []string {
903
+	var parsedResolvConf = StripComments(resolvConf, []byte("#"))
904
+	nameservers := []string{}
905
+	re := regexp.MustCompile(`^\s*nameserver\s*(([0-9]\.){3}([0-9]))\s*$`)
906
+	for _, line := range bytes.Split(parsedResolvConf, []byte("\n")) {
907
+		var ns = re.FindSubmatch(line)
908
+		if len(ns) > 0 {
909
+			nameservers = append(nameservers, string(ns[1])+"/32")
910
+		}
911
+	}
912
+
913
+	return nameservers
914
+}
915
+
899 916
 func ParseHost(host string, port int, addr string) (string, error) {
900 917
 	var proto string
901 918
 	switch {
... ...
@@ -444,3 +444,41 @@ func TestParsePortMapping(t *testing.T) {
444 444
 		t.Fail()
445 445
 	}
446 446
 }
447
+
448
+func TestGetNameserversAsCIDR(t *testing.T) {
449
+	for resolv, result := range map[string][]string{`
450
+nameserver 1.2.3.4
451
+nameserver 4.3.2.1
452
+search example.com`: {"1.2.3.4/32", "4.3.2.1/32"},
453
+		`search example.com`: {},
454
+		`nameserver 1.2.3.4
455
+search example.com
456
+nameserver 4.3.2.1`: []string{"1.2.3.4/32", "4.3.2.1/32"},
457
+    ``: []string{},
458
+    `  nameserver 1.2.3.4   `: []string{"1.2.3.4/32"},
459
+    `search example.com
460
+nameserver 1.2.3.4
461
+#nameserver 4.3.2.1`: []string{"1.2.3.4/32"},
462
+    `search example.com
463
+nameserver 1.2.3.4 # not 4.3.2.1`: []string{"1.2.3.4/32"},
464
+    } {
465
+        test := GetNameserversAsCIDR([]byte(resolv))
466
+        if !StrSlicesEqual(test, result) {
467
+            t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
468
+        }
469
+    }
470
+}
471
+
472
+func StrSlicesEqual(a, b []string) bool {
473
+	if len(a) != len(b) {
474
+		return false
475
+	}
476
+
477
+	for i, v := range a {
478
+		if v != b[i] {
479
+			return false
480
+		}
481
+	}
482
+
483
+	return true
484
+}
... ...
@@ -67,7 +67,7 @@ func newTestRuntime(prefix string) (runtime *Runtime, err error) {
67 67
 	}
68 68
 
69 69
 	config := &DaemonConfig{
70
-		GraphPath:   root,
70
+		Root:   root,
71 71
 		AutoRestart: false,
72 72
 	}
73 73
 	runtime, err = NewRuntimeFromDirectory(config)
... ...
@@ -116,7 +116,7 @@ func readFile(src string, t *testing.T) (content string) {
116 116
 // dynamically replaced by the current test image.
117 117
 // The caller is responsible for destroying the container.
118 118
 // Call t.Fatal() at the first error.
119
-func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig, error) {
119
+func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, error) {
120 120
 	config, hostConfig, _, err := ParseRun(args, nil)
121 121
 	defer func() {
122 122
 		if err != nil && t != nil {
... ...
@@ -124,16 +124,17 @@ func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConf
124 124
 		}
125 125
 	}()
126 126
 	if err != nil {
127
-		return nil, nil, err
127
+		return nil, err
128 128
 	}
129 129
 	if config.Image == "_" {
130 130
 		config.Image = GetTestImage(r).ID
131 131
 	}
132 132
 	c, _, err := r.Create(config, "")
133 133
 	if err != nil {
134
-		return nil, nil, err
134
+		return nil, err
135 135
 	}
136
-	return c, hostConfig, nil
136
+	c.hostConfig = hostConfig
137
+	return c, nil
137 138
 }
138 139
 
139 140
 // Create a test container, start it, wait for it to complete, destroy it,
... ...
@@ -146,7 +147,7 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e
146 146
 			t.Fatal(err)
147 147
 		}
148 148
 	}()
149
-	container, hostConfig, err := mkContainer(r, args, t)
149
+	container, err := mkContainer(r, args, t)
150 150
 	if err != nil {
151 151
 		return "", err
152 152
 	}
... ...
@@ -156,7 +157,7 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e
156 156
 		return "", err
157 157
 	}
158 158
 	defer stdout.Close()
159
-	if err := container.Start(hostConfig); err != nil {
159
+	if err := container.Start(); err != nil {
160 160
 		return "", err
161 161
 	}
162 162
 	container.Wait()