Compare commits

..

482 Commits
1.2.0 ... 1.4.1

Author SHA1 Message Date
Daniel Nephin
b24ca75914 Bump 1.4.1
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-09-14 18:18:01 -04:00
Daniel Nephin
2b75741e5a Fix cherry-pick errors.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-09-11 14:22:35 -04:00
Daniel Nephin
7ff8c2b224 Resolves #1804
Fix mutation of service.options when a label or environment variable is specified in the config.

Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-09-10 19:32:30 -04:00
Daniel Nephin
db31adc208 Extract link names into a function.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-09-10 19:32:30 -04:00
Daniel Nephin
805f6a7683 Refactor network_mode logic out of Service.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2015-09-10 19:32:30 -04:00
Daniel Nephin
d92f323e6d Fixes #1757 - include all service properties in the config_dict()
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-09-10 19:32:30 -04:00
Daniel Nephin
cf2dbf55b8 Cleanup some project logic.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-09-10 19:32:30 -04:00
Daniel Nephin
8d4c724c2d Sort config keys
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-09-10 19:32:30 -04:00
Daniel Nephin
9cb2770da4 Make external_links a regular service.option so that it's part of the config hash
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-09-10 19:32:30 -04:00
Daniel Nephin
6a23491fa9 Resolves #1856, fix regression in #1645. Includes some refactoring to make testing easier.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-09-10 19:32:30 -04:00
Mazz Mosley
294b9742be Handle all exceptions
If we get back an error that wasn't an APIError, it was causing the
thread to hang. This catch all, while I appreciate feels risky to
have a catch all, is better than not catching and silently failing,
with a never ending thread.

If something worse than an APIError has gone wrong, we want to stop
the incredible journey of what we're doing.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-09-10 19:32:30 -04:00
Aanand Prasad
a9b1f15f92 Fix volume path warning
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-09-10 19:32:29 -04:00
Aanand Prasad
28139ab90d Bump 1.4.0
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-08-11 16:00:20 +01:00
Aanand Prasad
d0792b49fa Merge pull request #1846 from aanand/fix-mem-limit-options
Fix mem_limit and memswap_limit regression
(cherry picked from commit 93cc7e3751)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-08-11 16:00:20 +01:00
Mazz Mosley
5548aa5c79 Merge pull request #1833 from aanand/deprecate-relative-volumes-without-dot
Show a warning when a relative path is specified without "./"
(cherry picked from commit 52733f6996)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>

Conflicts:
	compose/config.py
	tests/unit/config_test.py
2015-08-11 12:57:42 +01:00
Ben Firshman
16440ff055 Merge pull request #1829 from vlajos/typofixes-vlajos-20150807
typofix - https://github.com/vlajos/misspell_fixer
(cherry picked from commit b7baa899e2)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-08-11 09:44:28 +01:00
Ben Firshman
7850d6de45 Merge pull request #1832 from aanand/use-docker-1.8.0-rc3
Test against Docker 1.8.0 RC3
(cherry picked from commit afc9629c59)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-08-11 09:44:28 +01:00
Ben Firshman
74b4fb89bb Merge pull request #1835 from aanand/fix-crash-when-container-has-no-name
Ignore containers that don't have a name
(cherry picked from commit 4e12ce39b3)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-08-11 09:44:27 +01:00
Ben Firshman
22ccf35fa1 Merge pull request #1836 from aanand/use-overlay-driver-in-tests
Use overlay driver in tests
(cherry picked from commit 197d332620)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-08-11 09:44:27 +01:00
Aanand Prasad
7ad1fe24bd Merge pull request #1815 from aanand/abort-if-daemon-cant-start
Abort tests if daemon fails to start
(cherry picked from commit f7b9daf927)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-08-11 09:44:27 +01:00
Aanand Prasad
450ba978c1 Merge pull request #1812 from moxiegirl/install-update-for-1811
Closes #1811 for Toolbox
(cherry picked from commit 6cb8e512f2)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>

Conflicts:
	docs/install.md
2015-08-10 12:51:10 +01:00
Aanand Prasad
3d6946417d Merge pull request #1800 from aanand/volume-driver-support
Support volume_driver
(cherry picked from commit 41b9df7639)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-31 16:29:24 +01:00
Daniel Nephin
31cf63b374 Merge pull request #1799 from d2bit/clean-rails-quickstart-guide-db-config
Remove useless postgres 'port' configuration
(cherry picked from commit b25f05bed4)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-31 15:46:07 +01:00
Daniel Nephin
5c853c4a2c Merge pull request #1794 from aanand/add-test-for-trailing-slash-volume-copy
Add test for trailing slash volume copying bug
(cherry picked from commit ea7276031c)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-31 15:29:44 +01:00
Aanand Prasad
ad922cd7a1 Merge pull request #1787 from aanand/fix-duplicate-volume-bind
Fix "Duplicate volume mount" error when config has trailing slashes
(cherry picked from commit dc7bdd10d4)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-31 15:29:44 +01:00
Aanand Prasad
49bafdc4cd Merge pull request #1777 from aanand/update-api-version
Update API version to 1.19
(cherry picked from commit 276e369c31)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-31 15:29:44 +01:00
Aanand Prasad
989b2491b9 Merge pull request #1780 from gheart/specify_api_version_via_env
Allow API version specification via env var
(cherry picked from commit 2759ab5ab6)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-31 15:29:44 +01:00
Aanand Prasad
ca2ce3a034 Merge pull request #1779 from aanand/mac-binary-error-hint
Add hint about OS X binary compatibility
(cherry picked from commit 1496734cbb)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-31 15:29:43 +01:00
Aanand Prasad
dfe9dccab8 Merge pull request #1774 from moxiegirl/test-entire-build
Contributors can build public docs with compose docs in context
(cherry picked from commit 487eae3b7b)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-31 15:29:43 +01:00
Aanand Prasad
d456c3909d Merge remote-tracking branch 'origin/release' into bump-1.4.0 2015-07-23 16:46:57 +01:00
Aanand Prasad
e832e86f09 Merge pull request #1762 from aanand/update-changelog-and-install
Update CHANGES.md and install.md for latest stable version
2015-07-23 16:37:02 +01:00
Daniel Nephin
33a108f9bb Merge pull request #1760 from alunduil/add-completion-to-sdist
add all completions to sdist
2015-07-23 07:34:30 -07:00
Mazz Mosley
65cc84140a Merge pull request #1736 from aanand/deprecate-allow-insecure-ssl
Deprecate --allow-insecure-ssl
2015-07-23 14:09:17 +01:00
Aanand Prasad
227435f613 Update CHANGES.md and install.md for latest stable version
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-23 11:09:27 +01:00
Aanand Prasad
70c7d27c4e Merge pull request #1758 from mnowster/improve-scale-test-coverage
Improve scale test coverage
2015-07-23 11:07:48 +01:00
Aanand Prasad
04a773f1c8 Deprecate --allow-insecure-ssl
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-23 10:56:21 +01:00
Aanand Prasad
f4dac02947 Update docker-py to 1.3.1
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-23 10:56:21 +01:00
Mazz Mosley
2c8aade13e Space for errors
It was harder to see when there are errors if they came straight after
the other output. Putting a newline in there gives it a bit of visual
room.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-23 10:55:57 +01:00
Mazz Mosley
119901c19b Improve test coverage for scale
Also includes tiny amount of code cleanup, being explicit with imports.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-23 10:55:57 +01:00
Alex Brandt
ef44c46c72 add all completions to sdist
The zsh completion was recently added but missed from the sdist.  This
includes all completions that might be added at any point.

Signed-off-by: Alex Brandt <alunduil@alunduil.com>
2015-07-22 21:17:15 -05:00
Aanand Prasad
fc32ccefca Merge pull request #1755 from aanand/fix-container-name-one-off
Don't use custom name for one-off containers
2015-07-22 18:14:31 +01:00
Aanand Prasad
1739448402 Don't use custom name for one-off containers
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-22 15:39:56 +01:00
Mazz Mosley
620dac53f6 Merge pull request #1746 from aanand/remove-logging-test
Remove logging test
2015-07-21 16:34:58 +01:00
Aanand Prasad
dc62279d02 Merge pull request #1734 from mnowster/1653-scale-in-parallel
Parallelise scale
2015-07-21 16:02:04 +01:00
Aanand Prasad
4035a203fa Merge pull request #1745 from bfirsh/2015-07-21-roadmap-update
Update roadmap with state convergence
2015-07-21 16:01:13 +01:00
Aanand Prasad
233c509f71 Remove logging test
It doesn't do much other than cause the remainder of the test suite to
generate lots of junk output.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-21 15:56:37 +01:00
Mazz Mosley
e1c1a4c0aa Scale restarts stopped containers
This is existing behaviour and should be kept.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-21 15:41:22 +01:00
Ben Firshman
41406cdd68 Update roadmap with state convergence
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-07-21 15:37:55 +01:00
Mazz Mosley
cc0bfea8a1 Merge pull request #1711 from aanand/custom-names
Add container_name option for specifying a custom container name
2015-07-21 14:28:16 +01:00
Mazz Mosley
090879b67a Merge pull request #1737 from aanand/update-completion-for-force-recreate
Update bash and zsh completion for --force-recrate
2015-07-21 12:18:16 +01:00
Mazz Mosley
3ec7124539 Merge pull request #1742 from DoubleMalt/patch-1
Fix required version of websockets-client
2015-07-21 12:16:23 +01:00
Mazz Mosley
f721389447 Merge pull request #1743 from aanand/fix-stream-stack-trace
Stop printing a stack trace when there's an error when pulling
2015-07-21 12:15:18 +01:00
Mazz Mosley
da650e9cfd Refactor parallel execute
Refactored parallel execute and execute create into a single function
parallel_execute that can now handle both cases. This helps untangle it
from being so tightly coupled to the container.

Updated all the relevant operations to use the refactored function.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-21 11:56:59 +01:00
Aanand Prasad
38a6209acd Stop printing a stack trace when there's an error when pulling
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-21 11:07:20 +01:00
Christoph Witzany
04b7490ef2 Fix required version of websockets-client
Signed-off-by: Christoph Witzany <christoph@web.crofting.com>
2015-07-21 11:59:32 +02:00
Aanand Prasad
d1fdf1b809 Update bash and zsh completion for --force-recrate
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-20 17:18:39 +01:00
Mazz Mosley
5c29ded6ac Parallelise scale
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-20 16:33:04 +01:00
Mazz Mosley
85c90daa18 Merge pull request #1731 from aanand/fix-ctrl-c
Make parallel tasks interruptible with Ctrl-C
2015-07-20 16:05:26 +01:00
Aanand Prasad
4ba9d9dac2 Make parallel tasks interruptible with Ctrl-C
The concurrent.futures backport doesn't play well with
KeyboardInterrupt, so I'm using Thread and Queue instead.

Since thread pooling would likely be a pain to implement, I've just
removed `COMPOSE_MAX_WORKERS` for now. We'll implement it later if we
decide we need it.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-20 14:13:25 +01:00
Aanand Prasad
4ffae4a1ac Merge pull request #1725 from moxiegirl/update-docs-dockerfile
Updating with the latest image
2015-07-20 12:00:18 +01:00
Aanand Prasad
5f6d0d0824 Merge pull request #1721 from moxiegirl/remove-boot2docker-1720
Removing references to boot2docker
2015-07-20 11:52:15 +01:00
Aanand Prasad
05d8daa8e0 Merge pull request #1710 from mnowster/improve-output-for-parallel-executions
Improve output for parallel command
2015-07-20 11:49:37 +01:00
Aanand Prasad
9df2be80a8 Merge pull request #1717 from aanand/fix-up-docs
Fix regression in docs for 'up'
2015-07-20 11:45:50 +01:00
Mazz Mosley
9d9b865796 Add in error handling
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-20 10:21:49 +01:00
Mazz Mosley
61787fecea Resolve race condition
Sometimes, some messages were being executed at the same time, meaning
that the status wasn't being overwritten, it was displaying on a
separate line for both doing and done messages.

Rather than trying to have both sets of statuses being written out
concurrently, we write out all of the doing messages first. Then
the done messages are written out/updated, as they are completed.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-20 10:21:49 +01:00
Mary Anthony
949dd5b2c7 Updating with the latest image
Signed-off-by: Mary Anthony <mary@docker.com>
2015-07-19 16:08:01 -07:00
Mary Anthony
4ca210edd7 Removing references to boot2docker
- Replace with machine references
- 1.8 boot2docker is deprecated

Signed-off-by: Mary Anthony <mary@docker.com>
2015-07-17 16:17:46 -07:00
Aanand Prasad
4b4c4f37bd Merge pull request #1718 from moxiegirl/propos-alternates
remove cli
2015-07-17 17:25:08 +01:00
Mary Anthony
2407193594 remove cli
Signed-off-by: Mary Anthony <mary@docker.com>
2015-07-17 09:15:29 -07:00
Aanand Prasad
98eab03c61 Merge pull request #1068 from likeon/master
Docs: yml link in getting started with django
2015-07-17 17:11:38 +01:00
Aanand Prasad
faa7da6eff Merge pull request #1515 from aanand/allow-any-volume-mode
Allow any volume mode to be specified
2015-07-17 17:10:59 +01:00
Aanand Prasad
89f6caf871 Allow any volume mode to be specified
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-17 16:14:09 +01:00
Aanand Prasad
a3191ab90f Add container_name option
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-17 15:50:09 +01:00
Aanand Prasad
35092f1d5e Fix regression in docs for 'up'
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-17 15:29:02 +01:00
Aanand Prasad
445fe89fce Tweak wording of scale warning
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-17 15:24:17 +01:00
Aanand Prasad
9ffe69a572 Refactor can_be_scaled for clarity
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-17 15:24:17 +01:00
Aanand Prasad
6ca781416e Merge pull request #1715 from moxiegirl/propos-alternates
Updating Compose documentation
2015-07-17 15:23:53 +01:00
Mary Anthony
36f4d413fd Updating Compose documentation
- Split out commands into individual pages for maintainability
- Add full usage in commands pages for usability
- Updated description of the run command
- adding in Aanand's comment

Signed-off-by: Mary Anthony <mary@docker.com>
2015-07-17 07:05:45 -07:00
Mazz Mosley
c8643828d2 Merge pull request #1702 from aanand/smart-recreate-by-default
Smart recreate by default
2015-07-17 14:39:40 +01:00
Aanand Prasad
ced94a3504 Make smart recreate the default
Add --force-recreate flag to enable the old default behaviour of
recreating everything.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-17 11:28:02 +01:00
Aanand Prasad
b2cb5a48d2 Merge pull request #1714 from aanand/scale-timeout
Make scale timeout configurable, default to 10
2015-07-17 10:13:30 +01:00
Aanand Prasad
9d0bbdf8dd Make scale timeout configurable, default to 10
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-16 16:39:38 +01:00
Aanand Prasad
ca298309e0 Merge pull request #1713 from mnowster/increase-default-worker-number
Increase max-workers
2015-07-16 16:33:01 +01:00
Mazz Mosley
29f51f8216 Increase max-workers
There's significant speed improvement by having more workers. This
value still shouldn't cause anyone's machines to melt/explode.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-16 16:17:26 +01:00
Mazz Mosley
da6cbd4535 Improve output for parallel command
This approach takes the style of replacing the output message, in
place, when the command has finished executing. Bringing it a bit
more inline with what `docker pull` does.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-16 11:51:45 +01:00
Aanand Prasad
29ceef6d93 Merge pull request #1706 from aanand/bump-1.3.3
Bump 1.3.3
2015-07-16 11:40:20 +01:00
Aanand Prasad
8cff440800 Bump 1.3.3
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-16 11:21:01 +01:00
Mazz Mosley
e5f6ae767d Merge pull request #1704 from aanand/fix-timeout-type
Make sure up/restart/stop timeout is an int
(cherry picked from commit c7dccccd1f)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-16 11:19:21 +01:00
Mazz Mosley
c7dccccd1f Merge pull request #1704 from aanand/fix-timeout-type
Make sure up/restart/stop timeout is an int
2015-07-16 10:50:06 +01:00
Mazz Mosley
3a775388b2 Merge pull request #1698 from aanand/use-docker-1.7.1
Test against Docker 1.6.2 and 1.7.1
2015-07-16 10:48:24 +01:00
moxiegirl
947bce043e Merge pull request #1699 from bfirsh/link-to-waffle-board-from-contributing
Link to waffle board from CONTRIBUTING.md
2015-07-15 15:13:51 -07:00
Aanand Prasad
cd44179305 Merge pull request #1705 from aanand/fix-labels-null
Handle case where /containers/json returns "Labels": null
(cherry picked from commit 7b9664be8e)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-15 17:33:08 +01:00
Aanand Prasad
7b9664be8e Merge pull request #1705 from aanand/fix-labels-null
Handle case where /containers/json returns "Labels": null
2015-07-15 17:30:31 +01:00
Aanand Prasad
0c5c8d1f00 Handle case where /containers/json returns "Labels": null
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-15 17:13:33 +01:00
Mazz Mosley
5a46230555 Merge pull request #1703 from aanand/rename-max-workers
Rename max workers environment variable
2015-07-15 16:59:53 +01:00
Aanand Prasad
ec8bf066ba Make sure up/restart/stop timeout is an int
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-15 16:56:39 +01:00
Aanand Prasad
f03dec766d Rename max workers env var and fix docs
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-15 16:43:43 +01:00
Aanand Prasad
35aef1aee0 Merge pull request #1687 from mnowster/1651-parallelise-stopping-containers
1651 parallelise stopping containers
2015-07-15 16:29:54 +01:00
Aanand Prasad
6c7c63ce34 Merge pull request #1693 from ekristen/remove_tty_wrap
Do not wrap table cells
2015-07-15 16:27:27 +01:00
Aanand Prasad
e675d67774 Test against Docker 1.6.2 and 1.7.1
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-15 14:37:44 +01:00
Ben Firshman
200b408843 Link to waffle board from CONTRIBUTING.md
Closes #1067

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-07-15 11:56:46 +01:00
Mazz Mosley
03d34336a8 Document DEFAULT_MAX_WORKERS
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-15 11:56:06 +01:00
Aanand Prasad
847f839cab Merge pull request #1559 from aanand/update-swarm-docs
Update Swarm docs
2015-07-15 11:43:10 +01:00
Mazz Mosley
a68ca199a2 Execute container commands in parallel
Commands able to use this parallelisation are `stop`, `kill` and `rm`.

We're using a backported function from python 3, to allow us to make
the most of a pool of threads without having to write the low level
code for managing this ourselves.

A default value for number of threads is a low enough number so it
shouldn't cause performance problems but if someone knows the
capability of their system and wants to increase it, they can via
an environment variable DEFAULT_MAX_WORKERS

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-15 11:02:50 +01:00
Erik Kristensen
7937611366 change 80 to 0
Signed-off-by: Erik Kristensen <erik@erikkristensen.com>
2015-07-14 12:31:00 -06:00
Erik Kristensen
b7edb3ca9d do not wrap table cells
Signed-off-by: Erik Kristensen <erik@erikkristensen.com>
2015-07-14 12:30:42 -06:00
Aanand Prasad
c3c5b354b8 Merge pull request #1690 from aanand/bump-1.3.2
Bump 1.3.2
2015-07-14 18:04:22 +01:00
Aanand Prasad
95cf195dbd Bump 1.3.2
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-14 17:40:43 +01:00
Aanand Prasad
a80afd67ab Merge pull request #1688 from aanand/use-docker-py-1.3.0
Use docker-py 1.3.0
(cherry picked from commit 1e71eebc74)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-14 17:29:25 +01:00
Aanand Prasad
4bc4d273ac Merge pull request #1643 from aanand/warn-about-legacy-one-off-containers
Show an error on 'run' when there are legacy one-off containers
(cherry picked from commit 81707ef1ad)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-14 17:29:12 +01:00
Aanand Prasad
4911c77134 Merge pull request #1489 from dnephin/faster_integration_tests
Faster integration tests
(cherry picked from commit 5231288b4e)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>

Conflicts:
	compose/cli/main.py
2015-07-14 17:28:54 +01:00
Aanand Prasad
c1b9a76a54 Merge pull request #1658 from aanand/fix-smart-recreate-nonexistent-image
Fix smart recreate when 'image' is changed to something nonexistent
(cherry picked from commit 2bc10db545)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-14 17:25:33 +01:00
Aanand Prasad
c31e25af72 Merge pull request #1642 from aanand/fix-1573
Fix bug where duplicate container is leftover after 'up' fails
(cherry picked from commit f42fd6a3ad)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-14 17:25:03 +01:00
Aanand Prasad
c8295d36cc Merge pull request #1644 from aanand/fix-rm-bug
Stop 'rm' and 'ps' listing services not defined in the current file
(cherry picked from commit d85688892c)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-14 17:24:15 +01:00
Aanand Prasad
b12c29479e Merge pull request #1521 from dano/validate-service-names
Validate that service names passed to Project.containers aren't bogus.
(cherry picked from commit bc14c473c9)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-14 17:24:15 +01:00
Aanand Prasad
1e71eebc74 Merge pull request #1688 from aanand/use-docker-py-1.3.0
Use docker-py 1.3.0
2015-07-14 16:55:44 +01:00
Aanand Prasad
fdc34a187e Use docker-py 1.3.0
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-14 16:17:12 +01:00
Mazz Mosley
2db0a377e2 Minor test refactor
Rather than creating a docker client within each test, create one
at setup and make it accessible to the whole class.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-13 14:11:12 +01:00
Aanand Prasad
81707ef1ad Merge pull request #1643 from aanand/warn-about-legacy-one-off-containers
Show an error on 'run' when there are legacy one-off containers
2015-07-08 14:48:03 +01:00
Aanand Prasad
a8ad13734e Merge pull request #1666 from mnowster/docs-for-extends-file-default-behaviour
Docs for `file` default behaviour
2015-07-07 17:12:29 +01:00
Aanand Prasad
8b033d3946 Merge pull request #1665 from mnowster/1648-support-log-opt
1648 support log opt
2015-07-07 17:08:04 +01:00
Mazz Mosley
ef8ae07145 Docs for file default behaviour
Change in behaviour, `file` key is optional and if not set the
default is to look within the same file as `extends` is defined.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-07 17:01:09 +01:00
Mazz Mosley
6f31e8ebe9 Add support for log_opt
When specifying a log_driver you want to specify some options for
the logger as per the docker run --log-opt option. The logger
options are key value pairs.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-07 16:26:43 +01:00
Aanand Prasad
2bc10db545 Merge pull request #1658 from aanand/fix-smart-recreate-nonexistent-image
Fix smart recreate when 'image' is changed to something nonexistent
2015-07-07 16:12:44 +01:00
Aanand Prasad
531265bc84 Fix smart recreate when 'image' is changed to something nonexistent
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-07 16:01:44 +01:00
Mazz Mosley
c1223bfd3a Keep config keys in alphabetised order
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-07 12:02:05 +01:00
Aanand Prasad
e9da790f76 Merge pull request #1660 from mnowster/754-add-option-memswap-limit
Add in memswap_limit run option
2015-07-07 10:09:49 +01:00
Mazz Mosley
fc26982132 Add in memswap_limit run option
By allowing the memswap_limit option to be defined we also need to
check that mem_limit is set, you can't have swap without a limit.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-06 17:08:57 +01:00
Aanand Prasad
e98caf5cf9 Show an error on 'run' when there are legacy one-off containers
Also warn the user about the one-off containers in the standard error
message about legacy containers.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-06 14:45:17 +01:00
Daniel Nephin
20218394b9 Merge pull request #1649 from albers/doc-completion-zsh
Mention zsh command completion in general install section
2015-07-06 06:41:09 -07:00
Aanand Prasad
fc8f564558 Merge pull request #1632 from mnowster/extends_file_default_behaviour
Extends file default behaviour and fixes circular reference bug
2015-07-06 13:28:54 +01:00
Aanand Prasad
0fa5808389 Merge pull request #1623 from mnowster/documentation-fixes
Documentation fixes
2015-07-06 13:25:56 +01:00
Harald Albers
2fc7cd6e03 Mention zsh command completion in general install section
Signed-off-by: Harald Albers <github@albersweb.de>
2015-07-05 19:18:24 +02:00
Aanand Prasad
d90202399a Merge pull request #1645 from dnephin/fix_up_race
Fix race condition in docker-compose up
2015-07-03 16:28:22 +01:00
Aanand Prasad
f42fd6a3ad Merge pull request #1642 from aanand/fix-1573
Fix bug where duplicate container is leftover after 'up' fails
2015-07-03 16:25:53 +01:00
Aanand Prasad
d85688892c Merge pull request #1644 from aanand/fix-rm-bug
Stop 'rm' and 'ps' listing services not defined in the current file
2015-07-03 16:22:48 +01:00
Daniel Nephin
bd554a6fea Fix race condition in docker-compose up.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-07-03 10:29:46 -04:00
Mazz Mosley
317bbec98c Move make_service_dict out of config
This top level function is a test helper, so I've moved it into the
config_test file and updated accordingly.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 15:06:53 +01:00
Mazz Mosley
6e6dbdad95 working_dir is no longer optional
When building test data using make_service_dict, we need to include
working_dir as it is core to some of the functionality of
ServiceLoader.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 14:56:18 +01:00
Aanand Prasad
0b887b841f Stop 'rm' and 'ps' listing services not defined in the current file
If you have an alternate YAML file with different services defined,
containers for those services will be shown in `docker-compose ps` even
if you don't pass that file in.

Furthermore, `docker-compose rm` will claim that it's going to remove
them, but actually won't.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-03 14:34:27 +01:00
Mazz Mosley
c6e03d739d Test self referencing 'file'
When specifying the 'file' key to a value of it's own name, test
that this works and does not cause a false positive circular reference
error.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 14:14:47 +01:00
Mazz Mosley
6a6e7934bd Refactor circular-reference check
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 14:05:35 +01:00
Mazz Mosley
ba71e2a549 Use new load_from_filename
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 14:05:14 +01:00
Mazz Mosley
c51d53afba Fix off by one error
In our circular reference check the stack was previously off by one,
by not including the current service name that was calling another.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 12:24:33 +01:00
Mazz Mosley
bd7fcd1123 Use absolute paths
A circular reference bug occurs when there is a difference in the paths
of the file specified in the extends. So one time it is relative, second
time is absolute thus allowing a further circular reference to occur.

By using absolute paths we can be sure that the service filename check
is correct.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 12:21:12 +01:00
Mazz Mosley
254bc4908c Move extends validation into ServiceLoader class
This refactoring allows us to raise an error when there is no
'file' key specified in the .yml and no self.filename set. This
error was specific to the tests, as the tests are
the only place that constructs service dicts without sometimes
setting a filename.

Moving the function within the class as well as it is code that
is exclusively for the use of validating properties for the
ServiceLoader class.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 11:26:31 +01:00
Mazz Mosley
24c1d95869 Refactor extends validation tests
Split them out into individual validation tests so it is clearer
to see what is going on and to enable adding further validation
tests.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 11:26:31 +01:00
Mazz Mosley
5e2d43843c Reduce path manipulation
If we're using self.filename, then it's already a full path and we
don't need to splice off the filename.yml just so we can .join it
back together again.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 11:26:31 +01:00
Mazz Mosley
6e4a954dbd 'file' key can be omitted from extends
If the 'file' key is not set in the extends_options dict then we
look for the 'service' from within the same file.

Fixes this issue: https://github.com/docker/compose/issues/1237

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 11:26:30 +01:00
Mazz Mosley
31dedd8bdd Rename function process to validate
The process function contained purely validation checks, so re-named
the function to aid intent clarity.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-03 11:26:30 +01:00
Aanand Prasad
62b47224f0 Fix bug where duplicate container is leftover after 'up' fails
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-03 10:46:07 +01:00
Aanand Prasad
db7e5124f3 Merge pull request #1488 from dnephin/config_from_stdin
Support reading config from stdin
2015-07-03 09:03:40 +01:00
Aanand Prasad
a6b9982a1e Merge pull request #1633 from jeffk/parse_env_vars_in_all_volumes
Added env var parsing to volume container paths
2015-07-03 08:55:48 +01:00
Aanand Prasad
40b8c3c892 Merge pull request #1624 from mnowster/1519_relax_service_name_restrictions
1519 relax service name restrictions
2015-07-03 08:54:16 +01:00
Daniel Nephin
ae96fc0071 Support reading config from stdin.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-07-02 22:16:22 -04:00
Jeff Kramer
8709dc3c24 Added env var parsing to volume container paths
This commit adds environment variable parsing to the container side
of the volume mapping in configs. The common use case for this is
mounting SSH agent sockets in a container, using code like:

volumes:
    - $SSH_AUTH_SOCK:$SSH_AUTH_SOCK
environment:
    - SSH_AUTH_SOCK

Signed-off-by: Jeff Kramer <jeff.kramer@voxmedia.com>
2015-07-02 13:13:38 -05:00
Mazz Mosley
a8ea82f78f Improve link caption
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-02 15:39:22 +01:00
Mazz Mosley
cd2cdb25e3 Merge pull request #1631 from aanand/add-mazz-to-maintainers
Add Mazz to MAINTAINERS
2015-07-02 14:20:39 +01:00
moxiegirl
5185c9f70a Merge pull request #1619 from AndyWendt/patch-1
Added uninstall documentation for pip and curl
2015-07-02 06:07:59 -07:00
Mazz Mosley
f0dd63d5bc Don't use future tense
As per style-guide, future tense is not necessary.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 18:06:09 +01:00
Mazz Mosley
9549bd0539 Initial cap Docker
Use lowercase docker only when referencing a command/daemon

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 18:05:21 +01:00
Mazz Mosley
f33f673b49 Allow _.- chars in service names
As VALID_CHARS is shared with project names, these chars are also
now allowed within project names.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 17:57:50 +01:00
Mazz Mosley
8cf84ea552 Fix missing image names
These tests were indeed raising a config error, but not for the reason
intended/tested for. I've added in the image name so the config error
raise is correctly testing the Service name.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 17:57:50 +01:00
Mazz Mosley
8a47791161 Re-arrange for the more common case
boot2docker is the more common case so let's have that one first.
Also be more explicit and clear that the localhost:5000 is applicable
for people running on linux.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 16:20:29 +01:00
Mazz Mosley
ab03f2310b Include zsh shell
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 16:20:29 +01:00
Mazz Mosley
94ecc515d3 Link to docker's contrib workflow
This will aid people in how to make pull requests for code and docs.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 16:20:29 +01:00
Mazz Mosley
098ec3dcaa Add in git fork syncing links
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 15:58:11 +01:00
Mazz Mosley
4e0f4aa20a Split out release process
To aid clarity in how to contribute, I've moved the release process
out into it's own RELEASE_PROCESS.md file. The release process info
is only relevant for maintainers who are building new releases of
compose.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 15:58:11 +01:00
Mazz Mosley
c45c16cead Improve localhost & boot2docker info
Added a link to boot2docker for those unfamiliar with it.

http://0.0.0.0:5000 didn't resolve for me, however localhost:5000
did, so included reference to that.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 15:58:11 +01:00
Mazz Mosley
d0c499329e Add a note not to worry
When reading through the code for the first time and seeing redis,
those unfamiliar with docker first thought might be that they need
to install redis. Adding this sentence helps make it clearer that
this is not needed. Docker will take care of this in services.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 15:58:10 +01:00
Mazz Mosley
4c31741ac9 Include command completion link
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 15:58:10 +01:00
Mazz Mosley
a7a0888446 Re-phrasing for clarity
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 15:58:10 +01:00
Mazz Mosley
4d69a57edd Include flask output
When running `docker-compose up`, an extra line of output, from flask,
is outputted. I've included it so anyone new to docker-compose who
sees this output will know that it's expected and not worry that
something might have gone wrong.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 15:58:10 +01:00
Mazz Mosley
3906bd067e Remove redundant import
Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
2015-07-01 15:58:10 +01:00
Aanand Prasad
63941b8f6c Add Mazz to MAINTAINERS
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-07-01 15:38:07 +01:00
Aanand Prasad
75600e37ad Merge pull request #1605 from sdurrheimer/zsh-help-completion
Add --help to subcommands in zsh completion
2015-07-01 12:36:25 +01:00
Andy Wendt
8197d0e261 Added uninstall documentation for pip and curl
Signed-off-by: Andy Wendt <andy@awendt.com>
2015-06-30 10:27:44 -06:00
Steve Durrheimer
745e838673 Add --help to subcommands in zsh completion
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
2015-06-26 09:00:47 +02:00
Daniel Nephin
8346186469 Merge pull request #1603 from albers/completion-help-option
Add --help to bash completion
2015-06-25 17:59:06 -07:00
Harald Albers
9a8020d1bf Add --help to bash completion
Signed-off-by: Harald Albers <github@albersweb.de>
2015-06-25 14:04:03 -07:00
Aanand Prasad
bee65e8354 Merge pull request #1587 from aanand/fix-detached-description
Fix -d description
2015-06-23 08:36:02 -07:00
Aanand Prasad
104568b27b Merge branch 'compose-swarm-networking-guide' 2015-06-22 08:44:52 -07:00
Aanand Prasad
52975eca6f Fixes
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-22 08:44:41 -07:00
Aanand Prasad
cd47829f3d Merge pull request #1588 from aanand/bump-1.3.1
Bump 1.3.1
2015-06-22 08:01:13 -07:00
Aanand Prasad
4647875408 Merge pull request #1589 from aanand/compose-swarm-networking-guide
Add experimental Compose/Swarm/multi-host networking guide
2015-06-22 07:59:17 -07:00
Aanand Prasad
16213dd493 Add experimental Compose/Swarm/multi-host networking guide
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-22 07:58:08 -07:00
Aanand Prasad
4d4ef4e0b3 Bump 1.3.1
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-21 17:32:36 -07:00
Aanand Prasad
882ef2ccd8 Merge pull request #1578 from aanand/fix-migrate-help
Fix 'docker-compose help migrate-to-labels'
(cherry picked from commit c8751980f9)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-21 17:28:18 -07:00
Aanand Prasad
d6cd76c3c1 Merge pull request #1570 from aanand/fix-build-pull
Explicitly set pull=False when building
(cherry picked from commit 4f83a18912)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-21 17:28:09 -07:00
Ben Firshman
bd0be2cdc7 Merge pull request #1580 from aanand/dont-set-network-mode-when-none-is-specified
Don't set network mode when none is specified
(cherry picked from commit 911cd60360)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-21 17:27:59 -07:00
Aanand Prasad
c8751980f9 Merge pull request #1578 from aanand/fix-migrate-help
Fix 'docker-compose help migrate-to-labels'
2015-06-21 17:25:52 -07:00
Aanand Prasad
4f83a18912 Merge pull request #1570 from aanand/fix-build-pull
Explicitly set pull=False when building
2015-06-21 17:25:46 -07:00
Aanand Prasad
52c19bf96c Fix -d description
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-21 13:06:25 -07:00
Aanand Prasad
81cbf558c3 Merge pull request #1586 from aanand/replace-ticks-with-indent
Replace backtick code blocks with indentation
2015-06-21 13:03:19 -07:00
Aanand Prasad
511fc4a05c Replace backtick code blocks with indentation
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-21 12:37:20 -07:00
Ben Firshman
911cd60360 Merge pull request #1580 from aanand/dont-set-network-mode-when-none-is-specified
Don't set network mode when none is specified
2015-06-19 16:01:04 -07:00
Aanand Prasad
93372dd665 Fix 'docker-compose help migrate-to-labels'
- Fix "No such command" error

- Add text from migration section of install docs

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-19 15:43:53 -07:00
Aanand Prasad
c22cc02df5 Don't set network mode when none is specified
Setting a value overrides the new default network option.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-19 15:22:13 -07:00
Aanand Prasad
37ee6b0c19 Merge pull request #1575 from moxiegirl/fix-yaml-code
Updating from three ticks to code block
2015-06-19 11:55:57 -07:00
Aanand Prasad
efee2df310 Merge pull request #1572 from sdurrheimer/fix-completion-docs-urls
Fix completion docs URLs
2015-06-19 11:42:39 -07:00
Steve Durrheimer
d0102f0761 Fix completion docs URLs
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
2015-06-19 17:57:18 +02:00
Mary Anthony
c3df62472b Updating from three ticks to code block
Signed-off-by: Mary Anthony <mary@docker.com>
2015-06-19 02:28:09 -07:00
Aanand Prasad
bef0926c58 Explicitly set pull=False when building
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-18 17:43:16 -07:00
Daniel Nephin
c7c88bb4ff Merge pull request #1566 from aanand/bump-1.4.0dev
Bump 1.4.0dev
2015-06-18 19:57:53 -04:00
Aanand Prasad
5aa82a5519 Bump 1.4.0dev
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-18 11:56:43 -07:00
Aanand Prasad
a8d7ebd987 Merge pull request #1461 from aanand/bump-1.3.0
Bump 1.3.0
2015-06-18 11:41:40 -07:00
Aanand Prasad
00f61196a4 Bump 1.3.0
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-18 11:25:10 -07:00
Aanand Prasad
c21d6706b6 Merge pull request #1565 from aanand/use-docker-1.7.0
Use docker 1.7.0 and docker-py 1.2.3
(cherry picked from commit 8ffeaf2a54)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>

Conflicts:
	Dockerfile
2015-06-18 11:25:10 -07:00
Aanand Prasad
c3c5d91c47 Merge pull request #1563 from moxiegirl/hugo-test-fixes
Hugo final 1.7 Documentation PR -- please read carefully
(cherry picked from commit 4e73e86d94)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-18 11:25:10 -07:00
Aanand Prasad
8ffeaf2a54 Merge pull request #1565 from aanand/use-docker-1.7.0
Use docker 1.7.0 and docker-py 1.2.3
2015-06-18 11:23:40 -07:00
Aanand Prasad
ac56ef3d65 Update docker-py to 1.2.3 final
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-18 11:11:51 -07:00
Aanand Prasad
ae96e1af16 Use Docker 1.7.0
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-18 10:34:34 -07:00
Aanand Prasad
4e73e86d94 Merge pull request #1563 from moxiegirl/hugo-test-fixes
Hugo final 1.7 Documentation PR -- please read carefully
2015-06-18 10:20:10 -07:00
Mary Anthony
c26b1c8ee9 Entering fixes from Hugo
renaming compose-overview back to index
Updating with fixes per Aanand. And others found through test

Signed-off-by: Mary Anthony <mary@docker.com>
2015-06-17 21:50:43 -07:00
Aanand Prasad
77c939b256 Merge pull request #1142 from calou/master
Added support of option mac-address
2015-06-17 14:39:00 -07:00
Sébastien Gruchet
b76ac6e633 Added support to option mac-address
Signed-off-by: Sébastien Gruchet <gruchet@gmail.com>

Updated doc

Signed-off-by: Sébastien Gruchet <gruchet@gmail.com>

Fixed LINT errors

Signed-off-by: Sébastien Gruchet <gruchet@gmail.com>

Changed mac-address entry order in config keys

Signed-off-by: Sébastien Gruchet <gruchet@gmail.com>

Changed attributes order in docs/yml.md

Signed-off-by: Sébastien Gruchet <gruchet@gmail.com>
2015-06-17 21:28:17 +02:00
Daniel Nephin
26ea27172e Merge pull request #1558 from aanand/use-docker-1.7-rc4
Use Docker 1.7 RC5
2015-06-16 22:01:50 -04:00
Aanand Prasad
7fa4cd1214 Merge pull request #1552 from aanand/add-upgrade-instructions
Add upgrading instructions to install docs
(cherry picked from commit bc7161b475)
2015-06-16 16:29:18 -07:00
Aanand Prasad
bc7161b475 Merge pull request #1552 from aanand/add-upgrade-instructions
Add upgrading instructions to install docs
2015-06-16 16:26:40 -07:00
Aanand Prasad
e0af1a44ea Use Docker 1.7 RC5
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-16 15:14:48 -07:00
Aanand Prasad
bc14c473c9 Merge pull request #1521 from dano/validate-service-names
Validate that service names passed to Project.containers aren't bogus.
2015-06-16 10:45:49 -07:00
Dan O'Reilly
464ab3d727 Add a method specifically for service name validation.
Signed-off-by: Dan O'Reilly <oreilldf@gmail.com>
2015-06-15 23:06:06 -04:00
Daniel Nephin
f3df2a9fec Merge pull request #1551 from aanand/use-docker-1.7-rc3
Use Docker 1.7 RC3
2015-06-15 21:33:07 -04:00
Aanand Prasad
c421d23c34 Update Swarm docs
- Link to libnetwork

- Building now works, but is complicated by scaling

- Document how to set constraints/affinity

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 15:19:55 -07:00
Aanand Prasad
f353d9fbc0 Merge pull request #1406 from vdemeester/667-compose-port-scale
Fixing docker-compose port with scale (#667)
(cherry picked from commit 5b2a0cc73d)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:08 -07:00
Daniel Nephin
09018855ce Merge pull request #1550 from aanand/update-docker-py
Update setup.py with new docker-py minimum
(cherry picked from commit b3b44b8e4c)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:08 -07:00
Aanand Prasad
719954b02f Merge pull request #1545 from moxiegirl/test-tooling
Updated for new documentation tooling
(cherry picked from commit aaccd12d3d)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:08 -07:00
Daniel Nephin
67bc3fabe4 Merge pull request #1544 from aanand/fix-volume-deduping
Fix volume binds de-duplication
(cherry picked from commit 77e594dc94)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:08 -07:00
Daniel Nephin
e724a346c7 Merge pull request #1526 from aanand/remove-start-or-create-containers
Remove Service.start_or_create_containers()
(cherry picked from commit 38a11c4c28)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:08 -07:00
Daniel Nephin
87b4545b44 Merge pull request #1508 from thaJeztah/update-dockerproject-links
Update dockerproject.com links
(cherry picked from commit 417e6ce0c9)
2015-06-15 11:22:07 -07:00
Aanand Prasad
58a7844129 Merge pull request #1482 from bfirsh/add-build-and-dist-to-dockerignore
Make it possible to run tests remotely
(cherry picked from commit c8e096e089)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:07 -07:00
Daniel Nephin
4353f7b9f9 Merge pull request #1475 from fordhurley/patch-1
Fix markdown formatting for `--service-ports` example
(cherry picked from commit d64bf88e26)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:07 -07:00
Aanand Prasad
8f8693e13e Merge pull request #1480 from bfirsh/change-sigint-test-to-use-sigstop
Change kill SIGINT test to use SIGSTOP
(cherry picked from commit a15f996744)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:07 -07:00
Ben Firshman
363a6563c7 Merge pull request #1537 from aanand/reorder-service-utils
Reorder service.py utility methods
(cherry picked from commit e3525d64b5)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:07 -07:00
Aanand Prasad
59d6af73fa Merge pull request #1539 from bfirsh/add-image-affinity-to-test
Add image affinity to test script
(cherry picked from commit 4c2112dbfd)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:07 -07:00
Aanand Prasad
cd7f67018e Merge pull request #1466 from noironetworks/changing-scale-to-warning
Modified scale awareness from exception to warning
(cherry picked from commit 7d2a89427c)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:07 -07:00
Ben Firshman
b7e8770c4f Merge pull request #1538 from thieman/tnt-serivce-misspelled
Correct misspelling of "Service" in an error message
(cherry picked from commit bd246fb011)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:07 -07:00
Aanand Prasad
ad4cc5d6df Merge pull request #1497 from aanand/use-1.7-rc1
Run tests against Docker 1.7 RC2
(cherry picked from commit 0e9ccd36f3)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:06 -07:00
Aanand Prasad
ca14ed68f7 Merge pull request #1533 from edmorley/update-b2d-shellinit-example
Docs: Update boot2docker shellinit example to use 'eval'
(cherry picked from commit 17e03b29f9)
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:06 -07:00
Daniel Nephin
71514cb380 Merge pull request #1531 from aanand/test-crash-resilience
Test that data volumes now survive a crash when recreating
(cherry picked from commit 87c30ae6e4)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 11:22:06 -07:00
Aanand Prasad
5b2a0cc73d Merge pull request #1406 from vdemeester/667-compose-port-scale
Fixing docker-compose port with scale (#667)
2015-06-15 10:58:44 -07:00
Aanand Prasad
acd8dce595 Add upgrading instructions to install docs
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 10:46:48 -07:00
Daniel Nephin
b3b44b8e4c Merge pull request #1550 from aanand/update-docker-py
Update setup.py with new docker-py minimum
2015-06-15 13:43:57 -04:00
Aanand Prasad
aaccd12d3d Merge pull request #1545 from moxiegirl/test-tooling
Updated for new documentation tooling
2015-06-15 10:36:37 -07:00
Aanand Prasad
b4c49ed805 Use Docker 1.7 RC3
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 10:25:58 -07:00
Aanand Prasad
4e108e377e Update setup.py with new docker-py minimum
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-15 10:18:15 -07:00
Aanand Prasad
5231288b4e Merge pull request #1489 from dnephin/faster_integration_tests
Faster integration tests
2015-06-15 10:04:45 -07:00
Mary Anthony
e40fc02561 Testing with documentation tooling
Updating with changes
Updating for Hugo
Adding a README'
moving index.md compose-overview.md in links
changing overview
Updating image to pull

Signed-off-by: Mary Anthony <mary@docker.com>
2015-06-14 16:00:08 -07:00
Daniel Nephin
06db577105 Move converge() to a test module, and use a short timeout for tests.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-06-14 17:18:27 -04:00
Travis Thieman
c24d5380e6 Extend up -t to pass timeout to stop running containers
Signed-off-by: Travis Thieman <travis.thieman@gmail.com>
2015-06-14 16:45:28 -04:00
Daniel Nephin
60351a8e07 Speed up integration test and make cleanup easier by using labels
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-06-14 16:45:28 -04:00
Daniel Nephin
d827809ffb Use labels to filter containers.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-06-14 16:45:28 -04:00
Daniel Nephin
77e594dc94 Merge pull request #1544 from aanand/fix-volume-deduping
Fix volume binds de-duplication
2015-06-14 13:28:14 -04:00
Aanand Prasad
08bc4b830b Fix volume binds de-duplication
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-12 16:57:22 -04:00
Aanand Prasad
4c2112dbfd Merge pull request #1539 from bfirsh/add-image-affinity-to-test
Add image affinity to test script
2015-06-12 11:56:02 -04:00
Ben Firshman
ac222140e7 Add image affinity to test script
This will allow tests to be run on a Swarm. This is being fixed in
Swarm 0.4: https://github.com/docker/swarm/issues/743

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-06-11 15:58:07 -07:00
Ben Firshman
e3525d64b5 Merge pull request #1537 from aanand/reorder-service-utils
Reorder service.py utility methods
2015-06-11 21:55:33 +01:00
Ben Firshman
bd246fb011 Merge pull request #1538 from thieman/tnt-serivce-misspelled
Correct misspelling of "Service" in an error message
2015-06-11 21:54:53 +01:00
Travis Thieman
f31d4c8a93 Correct misspelling of "Service" in an error message
Signed-off-by: Travis Thieman <travis.thieman@gmail.com>
2015-06-11 14:03:08 -04:00
Aanand Prasad
7995fc2ed2 Reorder service.py utility methods
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-11 11:44:13 -04:00
Vincent Demeester
a5fd91c705 Fixing docker-compose port with scale (#667)
Fixes #667 and Closes #735 (taking over it)

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
2015-06-10 23:26:33 +02:00
Aanand Prasad
0e9ccd36f3 Merge pull request #1497 from aanand/use-1.7-rc1
Run tests against Docker 1.7 RC2
2015-06-10 17:19:24 -04:00
Aanand Prasad
4fd5d58076 Test against Docker 1.7.0 RC2
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-10 14:19:36 -04:00
Aanand Prasad
17e03b29f9 Merge pull request #1533 from edmorley/update-b2d-shellinit-example
Docs: Update boot2docker shellinit example to use 'eval'
2015-06-10 13:03:55 -04:00
Ed Morley
e3ba302627 Docs: Update boot2docker shellinit example to use 'eval'
The boot2docker documentation has since changed the recommended way to
use shellinit - see boot2docker/boot2docker#786.

Signed-off-by: Ed Morley <emorley@mozilla.com>
2015-06-10 14:38:35 +01:00
Daniel Nephin
87c30ae6e4 Merge pull request #1531 from aanand/test-crash-resilience
Test that data volumes now survive a crash when recreating
2015-06-09 22:22:56 -04:00
Daniel Nephin
8212f1bd45 Merge pull request #1529 from aanand/update-dockerpty
Update dockerpty to 0.3.4
(cherry picked from commit 95b2eaac04)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-09 18:24:14 -04:00
Daniel Nephin
95b2eaac04 Merge pull request #1529 from aanand/update-dockerpty
Update dockerpty to 0.3.4
2015-06-09 18:21:14 -04:00
Daniel Nephin
dca3bbdea3 Merge pull request #1527 from aanand/remove-logging-on-run-rm
Remove logging on run --rm
(cherry picked from commit 5578ccbb01)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-09 14:39:53 -04:00
Daniel Nephin
8ed7dfef6f Merge pull request #1525 from aanand/fix-duplicate-logging
Fix duplicate logging on up/run
(cherry picked from commit e2b790f732)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-09 14:39:52 -04:00
Daniel Nephin
631f5be02f Merge pull request #1481 from albers/completion-smart-recreate
Support --x-smart-recreate in bash completion
(cherry picked from commit 9a0bb325f2)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-09 14:39:52 -04:00
Ben Firshman
4f4ea2a402 Merge pull request #1325 from sdurrheimer/master
Zsh completion for docker-compose
(cherry picked from commit b638728d6c)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>

Conflicts:
	docs/completion.md
2015-06-09 14:39:50 -04:00
Aanand Prasad
5a5bffebd1 Merge pull request #1464 from twhiteman/bug1461
Possible division by zero error when pulling an image - fixes #1463
(cherry picked from commit d0e87929a1)

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-09 14:39:05 -04:00
Aanand Prasad
ff151c8ea0 Test that data volumes now survive a crash when recreating
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-09 13:06:09 -04:00
Aanand Prasad
66af37b135 Merge pull request #1474 from aanand/fix-ssl
Fix SSL "CERTIFICATE_VERIFY_FAILED" error
2015-06-09 11:56:27 -04:00
Dan O'Reilly
c59c9dd951 Add integration test for service name verification
Add a test to make sure NoSuchService is raised if
a bogus service name is given to 'docker-compose logs'.

Signed-off-by: Dan O'Reilly <oreilldf@gmail.com>
2015-06-08 17:04:42 -04:00
Aanand Prasad
ce880af821 Update dockerpty to 0.3.4
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-08 17:01:03 -04:00
Daniel Nephin
5578ccbb01 Merge pull request #1527 from aanand/remove-logging-on-run-rm
Remove logging on run --rm
2015-06-08 16:21:02 -04:00
Daniel Nephin
38a11c4c28 Merge pull request #1526 from aanand/remove-start-or-create-containers
Remove Service.start_or_create_containers()
2015-06-08 12:49:32 -04:00
Daniel Nephin
e2b790f732 Merge pull request #1525 from aanand/fix-duplicate-logging
Fix duplicate logging on up/run
2015-06-08 12:48:46 -04:00
Aanand Prasad
b6a7db787f Remove logging on run --rm
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-08 11:16:00 -04:00
Aanand Prasad
db2d02dc0b Remove Service.start_or_create_containers()
It's only used in a single test method.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-08 10:59:03 -04:00
Aanand Prasad
f59b43ac27 Fix duplicate logging on up/run
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-08 10:52:15 -04:00
dano
2527ef8055 Validate that service names passed to Project.containers aren't bogus.
Signed-off-by: Dan O'Reilly <oreilldf@gmail.com>
2015-06-06 15:47:16 -04:00
Daniel Nephin
417e6ce0c9 Merge pull request #1508 from thaJeztah/update-dockerproject-links
Update dockerproject.com links
2015-06-04 11:18:23 -05:00
Aanand Prasad
7d2a89427c Merge pull request #1466 from noironetworks/changing-scale-to-warning
Modified scale awareness from exception to warning
2015-06-04 16:21:01 +01:00
Aanand Prasad
036a4c4258 Merge pull request #1507 from albers/completion-version
Support version command in Bash completion
2015-06-04 15:46:50 +01:00
Sebastiaan van Stijn
cfcc12692f Update dockerproject.com links
The dockerproject.com domain is moving to dockerproject.org
this changes the buildstatus link to point to the new
domain.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2015-06-03 23:03:17 +02:00
Harald Albers
be92b79b42 Support version command in Bash completion
Signed-off-by: Harald Albers <github@albersweb.de>
2015-06-03 13:25:26 -07:00
Aanand Prasad
417e8d80c3 Merge pull request #1499 from sdurrheimer/master
Support version command in zsh completion
2015-06-02 15:05:46 +01:00
Aanand Prasad
8749bc0844 Build Python 2.7.9 in Docker image
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-02 11:39:17 +01:00
Aanand Prasad
f3d0c63db2 Make sure we use Python 2.7.9 and OpenSSL 1.0.1 when building OSX binary
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-02 11:39:17 +01:00
Aanand Prasad
93a846db31 Report Python and OpenSSL versions in --version output
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>

Conflicts:
	compose/cli/utils.py
2015-06-02 11:39:17 +01:00
Steve Durrheimer
77409737ce Support version command in zsh completion
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
2015-06-02 12:38:23 +02:00
Aanand Prasad
2594282082 Build Python 2.7.9 in Docker image
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-01 17:56:15 +01:00
Aanand Prasad
8ad11c0bc8 Make sure we use Python 2.7.9 and OpenSSL 1.0.1 when building OSX binary
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-01 17:55:57 +01:00
Aanand Prasad
c571bb485d Report Python and OpenSSL versions in --version output
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-06-01 17:55:31 +01:00
Aanand Prasad
8eb65ed946 Merge pull request #878 from funkyfuture/versioncmd
Enhanced version information and Docker-like version command
2015-06-01 13:36:11 +01:00
Daniel Nephin
9a0bb325f2 Merge pull request #1481 from albers/completion-smart-recreate
Support --x-smart-recreate in bash completion
2015-05-30 09:01:39 -05:00
Daniel Nephin
d64bf88e26 Merge pull request #1475 from fordhurley/patch-1
Fix markdown formatting for `--service-ports` example
2015-05-30 08:43:48 -05:00
Aanand Prasad
c8e096e089 Merge pull request #1482 from bfirsh/add-build-and-dist-to-dockerignore
Make it possible to run tests remotely
2015-05-29 17:12:57 +01:00
Ben Firshman
b638728d6c Merge pull request #1325 from sdurrheimer/master
Zsh completion for docker-compose
2015-05-29 14:45:21 +01:00
Steve Durrheimer
1d5526c71d Support --x-smart-recreate and -v in zsh completion
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
2015-05-29 14:24:55 +02:00
Steve Durrheimer
bc8d5923e7 Zsh completion for docker-compose
Signed-off-by: Steve Durrheimer <s.durrheimer@gmail.com>
2015-05-29 14:24:55 +02:00
Ben Firshman
a6bd1d22a0 Don't mount code in a volume when running tests
An image is built anyway, so this is unnecessary. This makes it
possible to run the tests on a remote Docker daemon.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-05-29 13:23:42 +01:00
Ben Firshman
c128e881c1 Add build and dist to dockerignore
These are the bulk of what gets sent in the build. Makes builds
much faster, particularly remotely.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-05-29 13:07:19 +01:00
Aanand Prasad
a15f996744 Merge pull request #1480 from bfirsh/change-sigint-test-to-use-sigstop
Change kill SIGINT test to use SIGSTOP
2015-05-29 12:38:40 +01:00
Harald Albers
b3c1c9c954 Support --x-smart-recreate and -v in bash completion
Signed-off-by: Harald Albers <github@albersweb.de>
2015-05-29 13:30:01 +02:00
Ben Firshman
ec437313a7 Change kill SIGINT test to use SIGSTOP
I think the original intention of the original test was the check that
different signals work, I think. This does this -- it sends a signal
that doesn't cause the container to stop.

Closes #759. Replaces #1467.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-05-29 11:13:37 +01:00
Ford Hurley
5945db0fa8 Fix markdown formatting for --service-ports example
Signed-off-by: Ford Hurley <ford.hurley@gmail.com>
2015-05-28 15:45:21 -04:00
Ben Firshman
8574cb67a4 Merge pull request #1471 from aanand/prepare-osx-script
Script to prepare OSX build environment
2015-05-28 13:58:14 +01:00
funkyfuture
ae9d619d86 Add command for Docker-style version information
This adds a command 'version' to show software versions information
like Docker does. In addition it includes:
- version of the docker-py-package
- Python-implementation and -version

Signed-off-by: Frank Sachsenheim <funkyfuture@riseup.net>
2015-05-28 00:51:10 +02:00
Aanand Prasad
686c25d50f Script to prepare OSX build environment
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-27 17:52:23 +01:00
Aanand Prasad
7d9aa8e0a9 Script to prepare OSX build environment
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-27 16:27:24 +01:00
Aanand Prasad
d0e87929a1 Merge pull request #1464 from twhiteman/bug1461
Possible division by zero error when pulling an image - fixes #1463
2015-05-27 12:49:58 +01:00
André Martins
ae63d35660 Modified scale awareness from exception to warning
Signed-off-by: André Martins <martins@noironetworks.com>
2015-05-26 23:59:45 +01:00
Todd Whiteman
b9c502531d Possible division by zero error when pulling an image - fixes #1463
Signed-off-by: Todd Whiteman <todd.whiteman@joyent.com>
2015-05-26 15:34:34 -07:00
Aanand Prasad
ef6555f084 Merge branch 'release' into bump-1.3.0 2015-05-26 17:45:28 +01:00
Aanand Prasad
1344099e29 Merge pull request #1444 from aanand/migrate-in-dependency-order
Migrate containers in dependency order
2015-05-26 17:30:14 +01:00
Daniel Nephin
48f3d41947 Merge pull request #1447 from aanand/fix-convergence-when-service-not-created
Fix regression in `docker-compose up`
2015-05-26 10:54:28 -05:00
Aanand Prasad
7da8e6be3b Migrate containers in dependency order
This fixes a bug where migration would fail with an error if a
downstream container was migrated before its upstream dependencies, due
to `check_for_legacy_containers()` being implicitly called when we fetch
`links`, `volumes_from` or `net` dependencies.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-26 16:03:06 +01:00
Aanand Prasad
4795fd874f Fix regression in docker-compose up
When an upstream dependency (e.g. a db) has a container but a downstream
service (e.g. a web app) doesn't, a web container is not created on
`docker-compose up`.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-26 16:01:05 +01:00
Aanand Prasad
276fee105b Merge pull request #1459 from bfirsh/update-description
Update description of Compose
2015-05-26 15:57:26 +01:00
Ben Firshman
8af4ae7935 Merge pull request #1441 from aanand/abort-on-legacy-containers
Bail out immediately if there are legacy containers
2015-05-26 15:45:08 +01:00
Ben Firshman
91ceb33d5a Update description of Compose
"Define and run multi-container applications with Docker"

Not just development environments, and "complex" is not clear and
not really true.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-05-26 15:42:55 +01:00
Aanand Prasad
0b4d9401ee Bail out immediately if there are legacy containers
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-26 11:53:51 +01:00
Daniel Nephin
889d3636f4 Merge pull request #1440 from aanand/legacy-fixes
Legacy fixes
2015-05-24 12:42:14 -05:00
Daniel Nephin
b0f945d2da Merge pull request #1432 from albers/completion-migrate_to_labels
bash completion for migrate_to_labels
2015-05-23 18:21:44 -05:00
Daniel Nephin
93c529182e Merge pull request #1446 from aanand/fix-create-logging
Fix missing logging on container creation
2015-05-21 17:34:54 -05:00
Harald Albers
412034a023 bash completion for migrate-to-labels
Signed-off-by: Harald Albers <github@albersweb.de>
2015-05-21 12:45:04 -07:00
Aanand Prasad
30c9e7323a Fix missing logging on container creation
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-21 20:06:25 +01:00
Aanand Prasad
051f56a1e6 Fix bugs with one-off legacy containers
- One-off containers were included in the warning log messages, which can
  make for unreadable output when there are lots (as there often are).

- Compose was attempting to recreate one-off containers as normal
  containers when migrating.

Fixed by implementing the exact naming logic from before we used labels.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-21 18:21:49 +01:00
Aanand Prasad
b5ce23885b Split out fetching of legacy names so we can test it
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-21 18:21:49 +01:00
Aanand Prasad
0fdb8bf814 Refactor migration logic
- Rename `migration` module to `legacy` to make its legacy-ness explicit

- Move `check_for_legacy_containers` into `legacy` module

- Fix migration test so it can be run in isolation

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-21 18:21:09 +01:00
Aanand Prasad
e538923545 Merge pull request #1442 from aanand/dashes-in-migration-command
Rename migrate_to_labels -> migrate-to-labels
2015-05-21 18:19:30 +01:00
Daniel Nephin
c0f65a9f4c Merge pull request #1445 from aanand/replace-sleep-with-top
Use 'top' instead of 'sleep' as a dummy command
2015-05-21 11:51:23 -05:00
Aanand Prasad
b0cb31c186 Use 'top' instead of 'sleep' as a dummy command
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-21 16:24:29 +01:00
Aanand Prasad
3080244c0b Rename migrate_to_labels -> migrate-to-labels
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-21 14:54:41 +01:00
Aanand Prasad
b183a66db1 Merge pull request #1437 from dnephin/fix_project_containers
Project.containers with service_names
2015-05-21 10:42:08 +01:00
Daniel Nephin
022f81711e Fixes #1434, Project.containers with service_names.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-05-20 20:47:34 -04:00
Aanand Prasad
4f40d0c168 Merge pull request #1433 from bfirsh/remove-whitespace-from-json-representation-of-container-config
Remove whitespace from json hash
2015-05-20 16:55:02 +01:00
Ben Firshman
f5ac1fa073 Remove whitespace from json hash
Reasoning:

e5d8447f06 (commitcomment-11243708)

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-05-20 16:02:08 +01:00
Ben Firshman
f79eb7b9ad Merge pull request #1382 from lsowen/security_opt
Add security_opt as a docker-compose.yml option
2015-05-20 13:40:42 +01:00
Aanand Prasad
b0b6ed31c4 Merge pull request #1430 from albers/fix-1426
Fix #1426 - migrate_to_labels not found
2015-05-20 12:31:18 +01:00
lsowen
ea7ee301c0 Add security_opt as a docker-compose.yml option
Signed-off-by: Logan Owen <lsowen@s1network.com>
2015-05-19 13:47:41 -04:00
Harald Albers
41315b32cb Fix #1426 - migrate_to_labels not found
Signed-off-by: Harald Albers <github@albersweb.de>
2015-05-19 16:37:50 +02:00
Aanand Prasad
80eaf4cc9f Merge pull request #1399 from aanand/state
Only recreate what's changed
2015-05-18 19:25:42 +01:00
Aanand Prasad
ef4eb66723 Implement smart recreate behind an experimental CLI flag
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-18 18:39:18 +01:00
Aanand Prasad
82bc7cd5ba Remove override_options arg from recreate_container(s)
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-18 17:42:09 +01:00
Aanand Prasad
3304c68891 Only set AttachStdin/out/err for one-off containers
If we're just streaming logs from `docker-compose up`, we don't need
to set AttachStdin/out/err, and doing so results in containers with
different configuration depending on whether `up` or `run` were invoked
with `-d` or not.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-18 17:41:04 +01:00
Aanand Prasad
1e6d912fbc Merge pull request #1356 from dnephin/use_labels_instead_of_names
Use labels instead of names to identify containers
2015-05-18 17:38:46 +01:00
Aanand Prasad
4ef3bbcdf2 Merge pull request #1415 from aanand/fix-run-race-condition
Fix race condition in `docker-compose run`
2015-05-18 16:16:31 +01:00
Daniel Nephin
62059d55e6 Add migration warning and option to migrate to labels.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-05-18 10:55:12 -04:00
Daniel Nephin
ed50a0a3a0 Resolves #1066, use labels to identify containers
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-05-18 10:47:26 -04:00
Daniel Nephin
28d2aff8b8 Fix teardown for integration tests.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-05-18 10:44:44 -04:00
Aanand Prasad
862971cffa Fix race condition in docker-compose run
We shouldn't start the container before handing it off to dockerpty -
dockerpty will start it after attaching, which is the correct order.
Otherwise the container might exit before we attach to it, which can
lead to weird bugs.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-15 12:16:24 +01:00
Daniel Nephin
c8022457eb Merge pull request #1413 from aanand/update-dockerpty
Update dockerpty to 0.3.3
2015-05-14 21:23:46 -04:00
Aanand Prasad
9bbf1a33d1 Update dockerpty to 0.3.3
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-14 20:03:50 +01:00
Aanand Prasad
0ac8c3cb03 Merge pull request #858 from dnephin/fix_volumes_recreate_on_1.4.1
Preserve individual volumes on recreate
2015-05-14 16:12:08 +01:00
Daniel Nephin
d5c9626040 Merge pull request #1411 from aanand/fix-extends-docs
Fix typo in extends.md
2015-05-14 09:53:38 -04:00
Aanand Prasad
ad9c5ad938 Fix typo in extends.md
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-14 10:48:35 +01:00
Daniel Nephin
70d2e64dfe Merge pull request #1407 from aanand/update-docker-py
Update docker-py to 1.2.2
2015-05-12 20:22:48 -04:00
Aanand Prasad
1dccd58209 Update docker-py to 1.2.2
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-05-12 18:51:45 +01:00
Aanand Prasad
e0103ac0d4 Merge pull request #1405 from bfirsh/link-to-getting-started-guides-from-each-page
Link to getting started guides from each page
2015-05-12 14:40:53 +01:00
Ben Firshman
4d745ab87a Link to getting started guides from each page
These are really hard to find.

Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-05-12 12:44:43 +01:00
Daniel Nephin
417d9c2d51 Use individual volumes for recreate instead of volumes_from
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-05-11 13:01:43 -04:00
Daniel Nephin
4997facbb4 Merge pull request #1400 from DanElbert/754-device_option
Added devices config handling and device HostConfig handling
2015-05-11 12:42:48 -04:00
delbert@umn.edu
df87bd91c8 Added devices configuration option
Signed-off-by: Dan Elbert <dan.elbert@gmail.com>
2015-05-11 10:50:58 -05:00
Aanand Prasad
1748b0f81a Merge pull request #1349 from dnephin/rename_instead_of_intermediate
Rename container when recreating it
2015-05-08 10:33:28 +01:00
Daniel Nephin
6829efd4d3 Resolves #874, Rename instead of use an intermediate.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-05-07 21:53:41 -04:00
Daniel Nephin
99f2a3a583 Merge pull request #1396 from albers/completion-doc
Fix markdown formatting issue
2015-05-07 14:04:16 -04:00
Daniel Nephin
0f2f9db6d8 Merge pull request #1388 from vdemeester/1303-log-driver-support
Add support for log-driver as a docker-compose.yml option
2015-05-07 12:00:42 -04:00
Harald Albers
d6223371d6 Fix markdown formatting issue
Signed-off-by: Harald Albers <github@albersweb.de>
2015-05-07 04:41:12 -07:00
Daniel Nephin
4817d5944c Merge pull request #1391 from albers/completion-extglob
Fix #1386 by ensuring that exglob is set in bash completion
2015-05-06 10:46:13 -04:00
Vincent Demeester
f626fc5ce8 Add support for log-driver in docker-compose.yml
Closes #1303

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
2015-05-06 13:18:58 +02:00
Harald Albers
1579a125a3 Ensure that exglob is set in bash completion
Signed-off-by: Harald Albers <github@albersweb.de>
2015-05-06 09:33:22 +02:00
Daniel Nephin
7fb9ec29c4 Merge pull request #1335 from chernjie/pid_readonly
docker-compose create --readonly
2015-05-05 20:50:01 -04:00
Daniel Nephin
f78e89f265 Merge pull request #1381 from sherter/help-fix
Show proper command in help text of build subcommand
2015-05-05 20:48:25 -04:00
CJ
b06294399a See #1335: Added --read-only
Signed-off-by: CJ <lim@chernjie.com>
2015-05-02 23:39:39 +08:00
Simon Herter
b8e0aed21c Show proper command in help text of build subcommand
The help text of the build subcommand suggested to use 'compose build' (instead of 'docker-compose build') to rebuild images.

Signed-off-by: Simon Herter <sim.herter@gmail.com>
2015-05-01 18:58:55 -04:00
Daniel Nephin
4bce388b51 Merge pull request #1376 from aanand/fix-build-non-ascii-filename
Make sure the build path we pass to docker-py is a binary string
2015-04-30 20:54:21 -04:00
Daniel Nephin
6c95eed781 Merge pull request #1269 from aanand/labels
Implement 'labels' option
2015-04-30 20:49:11 -04:00
Aanand Prasad
4f366d8355 Make sure the build path we pass to docker-py is a binary string
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-30 11:57:46 +01:00
Daniel Nephin
878d90febf Merge pull request #1374 from aanand/close-before-attaching
Close connection after building or pulling
2015-04-29 18:56:03 -04:00
Aanand Prasad
1a77feea3f Close connection before attaching on 'up' and 'run'
This ensures that the connection is not recycled, which can cause the
Docker daemon to complain if we've already performed another streaming
call such as doing a build.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-29 18:42:03 +01:00
Aanand Prasad
7e0ab0714f Merge pull request #1344 from dnephin/fix_pull_with_sha
Support image with ids instead of names
2015-04-29 16:46:34 +01:00
Aanand Prasad
2e6bc078fb Implement 'labels' option
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-29 16:45:18 +01:00
Daniel Nephin
3dd860f0ba Fix #923, support image with ids instead of names.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2015-04-29 10:13:18 -04:00
Daniel Nephin
de800dea0f Merge pull request #1370 from aanand/update-docker-version
Update Docker version to 1.6 stable
2015-04-29 10:03:13 -04:00
Aanand Prasad
fed4377ef6 Merge pull request #1351 from mchasal/1301-alphabetize_usage
Fix for #1301, Alphabetize Commands
2015-04-29 14:21:46 +01:00
Aanand Prasad
021bf46557 Update Docker version to 1.6 stable
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-29 13:58:32 +01:00
Aanand Prasad
b7e5116267 Merge pull request #1352 from bfirsh/add-irccloud-invite-link
Use cool new IRCCloud links for IRC channel
2015-04-29 13:25:25 +01:00
Daniel Nephin
9532e5a4f2 Merge pull request #1331 from xuxinkun/cpuset20150424
Add cpuset config.
2015-04-28 13:21:00 -04:00
Ben Firshman
e5a118e3ce Use cool new IRCCloud links for IRC channel
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-04-28 14:24:48 +01:00
Daniel Nephin
a631c1eddb Merge pull request #1357 from turtlemonvh/1350-extends_parent_build_directory_dne_error
Fix for #1350, nonexisting build path in parent section causes extending section to fail
2015-04-27 13:49:01 -04:00
Timothy Van Heest
855855a0e6 Fix for #1350, nonexisting build path in parent section causes extending section to fail
Signed-off-by: Timothy Van Heest <timothy.vanheest@gmail.com>
2015-04-27 10:55:30 -04:00
Daniel Nephin
b808674132 Merge pull request #1360 from aanand/remove-wercker
Remove wercker.yml
2015-04-27 10:17:41 -04:00
Ben Firshman
7e574fca71 Merge pull request #1358 from aanand/update-readme
Update README.md with changes to docs/index.md
2015-04-27 15:14:06 +01:00
Aanand Prasad
7d617d60bc Remove wercker.yml
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-27 15:10:01 +01:00
Aanand Prasad
da71e01d30 Merge pull request #1359 from aanand/remove-dco-validation
Remove DCO validation from CI script
2015-04-27 15:08:51 +01:00
Daniel Nephin
a89bc304f6 Merge pull request #1075 from KyleJamesWalker/master
Support alternate Dockerfile name.
2015-04-27 10:06:43 -04:00
Aanand Prasad
240495f07f Remove DCO validation from CI script
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-27 15:02:24 +01:00
Aanand Prasad
2e19887bf1 Update README.md with changes to docs/index.md
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-27 14:59:42 +01:00
Daniel Nephin
a982e516fc Merge pull request #1354 from xwisen/master
modified the release notes section the first[PR #972]to[PR #1088]
2015-04-27 09:56:46 -04:00
Ben Firshman
3af56e1602 Merge pull request #1200 from chanezon/1148-pat-paul
paulczar fixes plus example file
2015-04-27 14:54:14 +01:00
Aanand Prasad
16f8106149 Merge pull request #1158 from chernjie/addhosts
Add extra_hosts to yml configuration --add-hosts
2015-04-27 12:26:29 +01:00
CJ
86a08c00f2 See https://github.com/docker/compose/pull/1158#discussion_r29063218
Signed-off-by: CJ <lim@chernjie.com>
2015-04-27 14:07:21 +08:00
xwisen
0ca9fa8b2b modified the release notes section the first[PR #972]to[PR #1088]
Signed-off-by: xwisen <xwisen@gmail.com>
2015-04-26 13:16:23 +08:00
xuxinkun
688f82c1cf Add cpuset config.
Signed-off-by: xuxinkun <xuxinkun@gmail.com>
2015-04-26 00:14:52 +08:00
Michael Chase-Salerno
9a44708081 Fix for #1301, Alphabetize Commands
Signed-off-by: Michael Chase-Salerno <bratac@linux.vnet.ibm.com>
2015-04-24 20:45:18 +00:00
Daniel Nephin
89789c54ad Merge pull request #1232 from aleksandr-vin/add-parent-directories-search-for-default-compose-files
Add parent directories search for default compose-files
2015-04-24 13:12:24 -04:00
Kyle Walker
d17c4d27fa Support alternate Dockerfile name.
Signed-off-by: Kyle James Walker <KyleJamesWalker@gmail.com>
2015-04-24 08:30:36 -07:00
CJ
25ee3f0033 Remove extra s from --add-host
linting...
six.string_types
list-of-strings in examples
disallow extra_hosts support for list-of-dicts
A more thorough sets of tests for extra_hosts
Provide better examples
As per @aanand's [comment](https://github.com/docker/compose/pull/1158/files#r28326312)

  I think it'd be better to check `if not isinstance(extra_hosts_line,
  six.string_types)` and raise an error saying `extra_hosts_config must be
  either a list of strings or a string->string mapping`. We shouldn't need
  to do anything special with the list-of-dicts case.
order result to work with assert
use set() instead of sort()

Signed-off-by: CJ <lim@chernjie.com>
2015-04-24 09:21:29 +08:00
Thomas Desvenain
8098b65576 Fix when pyyaml has interpreted line as a dictionary
Added unit tests in build_extra_hosts + fix

Signed-off-by: CJ <lim@chernjie.com>
2015-04-24 09:21:21 +08:00
Sam Wing
fb81c37ca6 added the extra_hosts option to the yml configuration which exposes the --add-host flag from the docker client
Signed-off-by: Sam Wing <sampwing@gmail.com>
2015-04-23 21:54:59 +08:00
Daniel Nephin
e6ec76161d Merge pull request #1293 from mchasal/1224
1224: Check that image or build is specified.
2015-04-21 15:19:11 -04:00
Aanand Prasad
b317071cf3 Merge pull request #1205 from josephpage/run-rm-restart
[cli] run --rm overrides restart: always
2015-04-21 15:48:43 +01:00
Aanand Prasad
bb922d63f5 Merge pull request #1318 from aanand/fix-restart-timeout
Fix --timeout flag on restart, add tests for stop and restart
2015-04-21 15:07:07 +01:00
Aanand Prasad
2291fa2d45 Fix --timeout flag on restart, add tests for stop and restart
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-21 11:59:33 +01:00
Ben Firshman
3c6652c101 Merge pull request #1308 from aanand/update-docs-1.2.0
Update docs for 1.2.0
2015-04-17 10:35:06 -07:00
Aanand Prasad
43af1684c1 Update docs for 1.2.0
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-17 16:02:57 +01:00
Aanand Prasad
2cdde099fa Merge pull request #1297 from aanand/bump-1.3.0-dev
Bump 1.3.0dev
2015-04-16 18:02:59 +01:00
Aanand Prasad
310c7623f9 Bump 1.3.0dev
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-16 17:54:18 +01:00
Aanand Prasad
6e64802545 Merge pull request #1296 from aanand/merge-release-1.2.0
Merge release 1.2.0
2015-04-16 17:52:47 +01:00
Aanand Prasad
8b5015c10f Bump 1.2.0
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-16 17:48:28 +01:00
Aanand Prasad
ed549155b3 Merge pull request #1159 from aanand/bump-1.2.0
Bump 1.2.0
2015-04-16 17:46:47 +01:00
Michael Chase-Salerno
24a6c240fc Testcase for #1224, check that image or build is specified
Signed-off-by: Michael Chase-Salerno <bratac@linux.vnet.ibm.com>
2015-04-15 21:38:24 +00:00
Michael Chase-Salerno
15b763acdb Fix for #1224, check that image or build is specified
Signed-off-by: Michael Chase-Salerno <bratac@linux.vnet.ibm.com>
2015-04-15 02:03:02 +00:00
Daniel Nephin
3cd116b99d Merge pull request #1278 from albers/completion-run-user
Add bash completion for docker-compose run --user
2015-04-14 11:04:03 -04:00
Aanand Prasad
b559653c8c Merge pull request #1277 from fredlf/add-help-text
Adds Where to Get Help section
2015-04-14 15:16:11 +01:00
Harald Albers
5f17423d3e Add bash completion for docker-compose run --user
Signed-off-by: Harald Albers <github@albersweb.de>
2015-04-10 19:52:13 +02:00
Fred Lifton
2a442ec6d9 Adds Where to Get Help section
Signed-off-by: Fred Lifton <fred.lifton@docker.com>
2015-04-09 16:23:25 -07:00
Aleksandr Vinokurov
ceff5cb9ca Add parent directories search for default compose-files
Does not change directory to the parent with the compose-file found.
Works like passing '--file' or setting 'COMPOSE_FILE' with absolute path.
Resolves issue #946.

Signed-off-by: Aleksandr Vinokurov <aleksandr.vin@gmail.com>
2015-04-09 22:36:47 +00:00
Ben Firshman
4926f8aef6 Merge pull request #1261 from aanand/fix-vars-in-volume-paths
Fix vars in volume paths
2015-04-09 14:44:07 +01:00
Daniel Nephin
927115c3d4 Merge pull request #1271 from sdake/master
Remove stray print
2015-04-09 09:41:27 -04:00
Steven Dake
1d7247b67e Remove stray print
A previous commit introduced a stray print operation.  Remove it.

Signed-off-by: Steven Dake <stdake@cisco.com>
2015-04-08 12:49:37 -07:00
Ben Firshman
a1cd00e3f0 Merge pull request #1251 from aanand/extends-guide
Add tutorial and reference for `extends`
2015-04-08 15:47:39 +01:00
Aanand Prasad
fd568b389d Fix home directory and env expansion in volume paths
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-07 16:23:45 +01:00
Aanand Prasad
4f95e81c6d Merge pull request #1166 from spk/fix_example_env_file
Docs: fix env_file example
2015-04-07 15:52:10 +01:00
Aanand Prasad
619e783a05 Merge pull request #1011 from sdake/master
Add a --pid=host feature to expose the host PID space to the container
2015-04-07 13:49:53 +01:00
Aanand Prasad
f3f7f000fe Add tutorial and reference for extends
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-07 13:46:14 +01:00
Aanand Prasad
219751abc7 Merge pull request #1258 from fredlf/1.6-docs-updates
Prepping for 1.6 release.
2015-04-07 11:22:02 +01:00
Joseph Page
0b48e137e8 add unit tests for run --rm with restart
Signed-off-by: Joseph Page <joseph.page@rednet.io>
2015-04-07 10:18:25 +02:00
Fred Lifton
947742852e Prepping for 1.6 release.
Adds release notes and edits/revises new Compose in production doc.
2015-04-06 16:47:07 -07:00
Steven Dake
94277a3eb0 Add --pid=host support
Allow docker-compsoe to use the docker --pid=host API available in 1.17

Signed-off-by: Steven Dake <stdake@cisco.com>
2015-04-06 12:44:35 -07:00
Steven Dake
11a2100d53 Add a --pid=host feature to expose the host PID space to the container
Docker 1.5.0+ introduces a --pid=host feature which allows sharing of PID
namespaces between baremetal and containers.  This is useful for atomic
upgrades, atomic rollbacks, and monitoring.

For more details of a real-life use case, check out:
http://sdake.io/2015/01/28/an-atomic-upgrade-process-for-openstack-compute-nodes/

Signed-off-by: Steven Dake <stdake@cisco.com>
2015-04-06 11:45:37 -07:00
Fred Lifton
530d7af5cf Merge pull request #1253 from aanand/production-guide
Add guide to using Compose in production
2015-04-06 10:49:49 -07:00
Aanand Prasad
502d58abe6 Add guide to using Compose in production
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-03 18:56:29 -04:00
Fred Lifton
eb073c53f4 Merge pull request #1249 from asveepay/update_doc
Update install docs for permission denied error
2015-04-03 12:31:38 -07:00
Roland Cooper
d866415b9a Update install docs for permission denied error
Signed-off-by: Roland Cooper <rcooper@enova.com>
2015-04-03 12:21:15 -05:00
Aanand Prasad
dd40658f87 Merge pull request #1238 from aanand/use-docker-1.6-rc3
Use Docker 1.6 RC3
2015-04-03 13:00:49 -04:00
Aanand Prasad
b3382ffd4f Use Docker 1.6 RC4
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-04-03 10:57:28 -04:00
Aanand Prasad
15a0fac939 Merge pull request #1141 from bfirsh/contributing-section-in-readme
Add contributing section to readme
2015-04-03 10:56:06 -04:00
Laurent Arnoud
e3cff5d17d Docs: fix env_file example
Thanks-to: @aanand
Signed-off-by: Laurent Arnoud <laurent@spkdev.net>
2015-04-01 11:15:09 +02:00
Daniel Nephin
0f70b8638f Merge pull request #1213 from moysesb/relative_build
Make value of 'build:' relative to the yml file.
2015-03-31 21:20:02 -04:00
Moysés Borges
8584525e8d Interpret 'build:' as relative to the yml file
* This fix introduces one side-effect: the build parameter is now
validated early, when the service dicionary is first constructed.
That leads to less scary stack traces when the path is not valid.

* The tests for the changes introduced here alter the fixtures
of those (otherwise unrelated) tests that make use of the 'build:'
parameter)

Signed-off-by: Moysés Borges Furtado <moyses.furtado@wplex.com.br>
2015-03-31 18:47:26 -03:00
Aanand Prasad
e3e2247159 Merge pull request #1231 from aanand/docker-1.6rc2
Test against Docker 1.6 RC2 only
2015-03-31 16:31:03 -04:00
Aanand Prasad
0650c4485a Test against Docker 1.6 RC2 only
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-31 16:04:22 -04:00
Aanand Prasad
e708f4f59d Merge pull request #1226 from aanand/merge-multi-value-options
Merge multi-value options when extending
2015-03-31 16:01:22 -04:00
Aanand Prasad
907918b492 Merge multi-value options when extending
Closes #1143.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-31 15:30:59 -04:00
Aanand Prasad
6dbe321a45 Merge pull request #1225 from aanand/fix-1222
When extending, `build` replaces `image` and vice versa
2015-03-31 15:23:34 -04:00
Aanand Prasad
2a415ede08 When extending, build replaces image and vice versa
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-30 17:14:19 -04:00
Ben Firshman
43369cda9c Merge pull request #1221 from aanand/update-swarm-doc
Update Swarm doc
2015-03-30 18:08:16 +01:00
Aanand Prasad
a2557a3354 Merge pull request #1198 from funkyfuture/reformat-contributing.md
Reformat CONTRIBUTING.md
2015-03-30 12:08:56 -04:00
Aanand Prasad
1a14449fe6 Update Swarm doc
- Co-scheduling will now work, so we can remove the stuff about
  `volumes_from` and `net` and manual affinity filters.

- Added a section about building.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-30 11:34:26 -04:00
Joseph Page
0b89ae6f20 [cli] run --rm overrides restart: always
docker/compose#1013

Signed-off-by: Joseph Page <joseph.page@rednet.io>
2015-03-30 14:45:24 +02:00
Patrick Chanezon
cec6dc28bb implemented @fredl suggestions
Signed-off-by: Patrick Chanezon <patlist@chanezon.com>
2015-03-27 17:12:29 -07:00
Aanand Prasad
853ce255ea Merge pull request #1202 from aanand/jenkins-script
WIP: Jenkins script
2015-03-27 14:59:49 -07:00
Aanand Prasad
db852e14e4 Add script/ci
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-27 14:39:32 -07:00
Patrick Chanezon
98dd0cd1f8 implemented @aanand comments
Signed-off-by: Patrick Chanezon <patlist@chanezon.com>
2015-03-27 13:26:51 -07:00
Patrick Chanezon
c441ac90d6 paulczar fixes plus example file
Signed-off-by: Patrick Chanezon <patlist@chanezon.com>
2015-03-26 16:35:53 -07:00
Aanand Prasad
9d7b54d8fd Merge pull request #1183 from pborreli/patch-2
Fixed typo
2015-03-26 12:31:07 -07:00
Pascal Borreli
59f04c6e29 Fixed typo
Signed-off-by: Pascal Borreli <pascal@borreli.com>
2015-03-26 19:03:06 +00:00
Aanand Prasad
367ae0c848 Merge pull request #1185 from akoskaaa/master
Make test files and config files pep8 valid
2015-03-26 10:04:37 -07:00
akoskaaa
4e0f555c58 make flake8 a bit more specific
Signed-off-by: akoskaaa <akos.hochrein@prezi.com>
2015-03-26 09:09:15 -07:00
Ben Firshman
baf18decae Merge pull request #1179 from aanand/test-1.6-rc
Add Docker 1.6 RC2 to tested versions
2015-03-26 14:18:36 +00:00
funkyfuture
826b8ca4d3 Reformat CONTRIBUTING.md
- some reformatting to make it better readable in smaller terminals
- adds a note that suggests validating DCO before pushing

Signed-off-by: funkyfuture <funkyfuture@riseup.net>
2015-03-26 13:11:05 +01:00
akoskaaa
fa2fb6bd38 [pep8] flake8 run for everything, fix items from this change
Signed-off-by: akoskaaa <akos.hochrein@prezi.com>
2015-03-25 23:15:34 -07:00
akoskaaa
f9ea5ecf40 [pep8] make test files and config files pep8 valid
Signed-off-by: akoskaaa <akos.hochrein@prezi.com>
2015-03-25 20:20:38 -07:00
Aanand Prasad
99f7eba930 Add Docker 1.6 RC2 to tested versions
Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
2015-03-25 14:48:39 -07:00
Ben Firshman
e1b27acd02 Add contributing section to readme
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
2015-03-25 17:22:04 +01:00
likeon
f393447ac9 Docs: yml link in getting started with django
Signed-off-by: Alexander Sterchov i@likeon.name
2015-03-13 13:50:20 +03:00
112 changed files with 6691 additions and 1844 deletions

View File

@@ -1,2 +1,4 @@
.git
build
dist
venv

View File

@@ -1,6 +1,138 @@
Change log
==========
1.4.1 (2015-09-10)
------------------
The following bugs have been fixed:
- Some configuration changes (notably changes to `links`, `volumes_from`, and
`net`) were not properly triggering a container recreate as part of
`docker-compose up`.
- `docker-compose up <service>` was showing logs for all services instead of
just the specified services.
- Containers with custom container names were showing up in logs as
`service_number` instead of their custom container name.
- When scaling a service sometimes containers would be recreated even when
the configuration had not changed.
1.4.0 (2015-08-04)
------------------
- By default, `docker-compose up` now only recreates containers for services whose configuration has changed since they were created. This should result in a dramatic speed-up for many applications.
The experimental `--x-smart-recreate` flag which introduced this feature in Compose 1.3.0 has been removed, and a `--force-recreate` flag has been added for when you want to recreate everything.
- Several of Compose's commands - `scale`, `stop`, `kill` and `rm` - now perform actions on multiple containers in parallel, rather than in sequence, which will run much faster on larger applications.
- You can now specify a custom name for a service's container with `container_name`. Because Docker container names must be unique, this means you can't scale the service beyond one container.
- You no longer have to specify a `file` option when using `extends` - it will default to the current file.
- Service names can now contain dots, dashes and underscores.
- Compose can now read YAML configuration from standard input, rather than from a file, by specifying `-` as the filename. This makes it easier to generate configuration dynamically:
$ echo 'redis: {"image": "redis"}' | docker-compose --file - up
- There's a new `docker-compose version` command which prints extended information about Compose's bundled dependencies.
- `docker-compose.yml` now supports `log_opt` as well as `log_driver`, allowing you to pass extra configuration to a service's logging driver.
- `docker-compose.yml` now supports `memswap_limit`, similar to `docker run --memory-swap`.
- When mounting volumes with the `volumes` option, you can now pass in any mode supported by the daemon, not just `:ro` or `:rw`. For example, SELinux users can pass `:z` or `:Z`.
- You can now specify a custom volume driver with the `volume_driver` option in `docker-compose.yml`, much like `docker run --volume-driver`.
- A bug has been fixed where Compose would fail to pull images from private registries serving plain (unsecured) HTTP. The `--allow-insecure-ssl` flag, which was previously used to work around this issue, has been deprecated and now has no effect.
- A bug has been fixed where `docker-compose build` would fail if the build depended on a private Hub image or an image from a private registry.
- A bug has been fixed where Compose would crash if there were containers which the Docker daemon had not finished removing.
- Two bugs have been fixed where Compose would sometimes fail with a "Duplicate bind mount" error, or fail to attach volumes to a container, if there was a volume path specified in `docker-compose.yml` with a trailing slash.
Thanks @mnowster, @dnephin, @ekristen, @funkyfuture, @jeffk and @lukemarsden!
1.3.3 (2015-07-15)
------------------
Two regressions have been fixed:
- When stopping containers gracefully, Compose was setting the timeout to 0, effectively forcing a SIGKILL every time.
- Compose would sometimes crash depending on the formatting of container data returned from the Docker API.
1.3.2 (2015-07-14)
------------------
The following bugs have been fixed:
- When there were one-off containers created by running `docker-compose run` on an older version of Compose, `docker-compose run` would fail with a name collision. Compose now shows an error if you have leftover containers of this type lying around, and tells you how to remove them.
- Compose was not reading Docker authentication config files created in the new location, `~/docker/config.json`, and authentication against private registries would therefore fail.
- When a container had a pseudo-TTY attached, its output in `docker-compose up` would be truncated.
- `docker-compose up --x-smart-recreate` would sometimes fail when an image tag was updated.
- `docker-compose up` would sometimes create two containers with the same numeric suffix.
- `docker-compose rm` and `docker-compose ps` would sometimes list services that aren't part of the current project (though no containers were erroneously removed).
- Some `docker-compose` commands would not show an error if invalid service names were passed in.
Thanks @dano, @josephpage, @kevinsimper, @lieryan, @phemmer, @soulrebel and @sschepens!
1.3.1 (2015-06-21)
------------------
The following bugs have been fixed:
- `docker-compose build` would always attempt to pull the base image before building.
- `docker-compose help migrate-to-labels` failed with an error.
- If no network mode was specified, Compose would set it to "bridge", rather than allowing the Docker daemon to use its configured default network mode.
1.3.0 (2015-06-18)
------------------
Firstly, two important notes:
- **This release contains breaking changes, and you will need to either remove or migrate your existing containers before running your app** - see the [upgrading section of the install docs](https://github.com/docker/compose/blob/1.3.0rc1/docs/install.md#upgrading) for details.
- Compose now requires Docker 1.6.0 or later.
We've done a lot of work in this release to remove hacks and make Compose more stable:
- Compose now uses container labels, rather than names, to keep track of containers. This makes Compose both faster and easier to integrate with your own tools.
- Compose no longer uses "intermediate containers" when recreating containers for a service. This makes `docker-compose up` less complex and more resilient to failure.
There are some new features:
- `docker-compose up` has an **experimental** new behaviour: it will only recreate containers for services whose configuration has changed in `docker-compose.yml`. This will eventually become the default, but for now you can take it for a spin:
$ docker-compose up --x-smart-recreate
- When invoked in a subdirectory of a project, `docker-compose` will now climb up through parent directories until it finds a `docker-compose.yml`.
Several new configuration keys have been added to `docker-compose.yml`:
- `dockerfile`, like `docker build --file`, lets you specify an alternate Dockerfile to use with `build`.
- `labels`, like `docker run --labels`, lets you add custom metadata to containers.
- `extra_hosts`, like `docker run --add-host`, lets you add entries to a container's `/etc/hosts` file.
- `pid: host`, like `docker run --pid=host`, lets you reuse the same PID namespace as the host machine.
- `cpuset`, like `docker run --cpuset-cpus`, lets you specify which CPUs to allow execution in.
- `read_only`, like `docker run --read-only`, lets you mount a container's filesystem as read-only.
- `security_opt`, like `docker run --security-opt`, lets you specify [security options](https://docs.docker.com/reference/run/#security-configuration).
- `log_driver`, like `docker run --log-driver`, lets you specify a [log driver](https://docs.docker.com/reference/run/#logging-drivers-log-driver).
Many bugs have been fixed, including the following:
- The output of `docker-compose run` was sometimes truncated, especially when running under Jenkins.
- A service's volumes would sometimes not update after volume configuration was changed in `docker-compose.yml`.
- Authenticating against third-party registries would sometimes fail.
- `docker-compose run --rm` would fail to remove the container if the service had a `restart` policy in place.
- `docker-compose scale` would refuse to scale a service beyond 1 container if it exposed a specific port number on the host.
- Compose would refuse to create multiple volume entries with the same host path.
Thanks @ahromis, @albers, @aleksandr-vin, @antoineco, @ccverak, @chernjie, @dnephin, @edmorley, @fordhurley, @josephpage, @KyleJamesWalker, @lsowen, @mchasal, @noironetworks, @sdake, @sdurrheimer, @sherter, @stephenlawrence, @thaJeztah, @thieman, @turtlemonvh, @twhiteman, @vdemeester, @xuxinkun and @zwily!
1.2.0 (2015-04-16)
------------------

View File

@@ -1,6 +1,8 @@
# Contributing to Compose
Compose is a part of the Docker project, and follows the same rules and principles. Take a read of [Docker's contributing guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) to get an overview.
Compose is a part of the Docker project, and follows the same rules and
principles. Take a read of [Docker's contributing guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md)
to get an overview.
## TL;DR
@@ -17,60 +19,45 @@ If you're looking contribute to Compose
but you're new to the project or maybe even to Python, here are the steps
that should get you started.
1. Fork [https://github.com/docker/compose](https://github.com/docker/compose) to your username.
1. Clone your forked repository locally `git clone git@github.com:yourusername/compose.git`.
1. Enter the local directory `cd compose`.
1. Set up a development environment by running `python setup.py develop`. This will install the dependencies and set up a symlink from your `docker-compose` executable to the checkout of the repository. When you now run `docker-compose` from anywhere on your machine, it will run your development version of Compose.
1. Fork [https://github.com/docker/compose](https://github.com/docker/compose)
to your username.
2. Clone your forked repository locally `git clone git@github.com:yourusername/compose.git`.
3. You must [configure a remote](https://help.github.com/articles/configuring-a-remote-for-a-fork/) for your fork so that you can [sync changes you make](https://help.github.com/articles/syncing-a-fork/) with the original repository.
4. Enter the local directory `cd compose`.
5. Set up a development environment by running `python setup.py develop`. This
will install the dependencies and set up a symlink from your `docker-compose`
executable to the checkout of the repository. When you now run
`docker-compose` from anywhere on your machine, it will run your development
version of Compose.
## Submitting a pull request
See Docker's [basic contribution workflow](https://docs.docker.com/project/make-a-contribution/#the-basic-contribution-workflow) for a guide on how to submit a pull request for code or documentation.
## Running the test suite
Use the test script to run linting checks and then the full test suite:
Use the test script to run linting checks and then the full test suite against
different Python interpreters:
$ script/test
Tests are run against a Docker daemon inside a container, so that we can test against multiple Docker versions. By default they'll run against only the latest Docker version - set the `DOCKER_VERSIONS` environment variable to "all" to run against all supported versions:
Tests are run against a Docker daemon inside a container, so that we can test
against multiple Docker versions. By default they'll run against only the latest
Docker version - set the `DOCKER_VERSIONS` environment variable to "all" to run
against all supported versions:
$ DOCKER_VERSIONS=all script/test
Arguments to `script/test` are passed through to the `nosetests` executable, so you can specify a test directory, file, module, class or method:
Arguments to `script/test` are passed through to the `nosetests` executable, so
you can specify a test directory, file, module, class or method:
$ script/test tests/unit
$ script/test tests/unit/cli_test.py
$ script/test tests.integration.service_test
$ script/test tests.integration.service_test:ServiceTest.test_containers
## Building binaries
## Finding things to work on
Linux:
We use a [Waffle.io board](https://waffle.io/docker/compose) to keep track of specific things we are working on and planning to work on. If you're looking for things to work on, stuff in the backlog is a great place to start.
$ script/build-linux
OS X:
$ script/build-osx
Note that this only works on Mountain Lion, not Mavericks, due to a [bug in PyInstaller](http://www.pyinstaller.org/ticket/807).
## Release process
1. Open pull request that:
- Updates the version in `compose/__init__.py`
- Updates the binary URL in `docs/install.md`
- Updates the script URL in `docs/completion.md`
- Adds release notes to `CHANGES.md`
2. Create unpublished GitHub release with release notes
3. Build Linux version on any Docker host with `script/build-linux` and attach to release
4. Build OS X version on Mountain Lion with `script/build-osx` and attach to release as `docker-compose-Darwin-x86_64` and `docker-compose-Linux-x86_64`.
5. Publish GitHub release, creating tag
6. Update website with `script/deploy-docs`
7. Upload PyPi package
$ git checkout $VERSION
$ python setup.py sdist upload
For more information about our project planning, take a look at our [GitHub wiki](https://github.com/docker/compose/wiki).

View File

@@ -3,9 +3,11 @@ FROM debian:wheezy
RUN set -ex; \
apt-get update -qq; \
apt-get install -y \
python \
python-pip \
python-dev \
gcc \
make \
zlib1g \
zlib1g-dev \
libssl-dev \
git \
apt-transport-https \
ca-certificates \
@@ -15,16 +17,47 @@ RUN set -ex; \
; \
rm -rf /var/lib/apt/lists/*
ENV ALL_DOCKER_VERSIONS 1.3.3 1.4.1 1.5.0
# Build Python 2.7.9 from source
RUN set -ex; \
curl -LO https://www.python.org/ftp/python/2.7.9/Python-2.7.9.tgz; \
tar -xzf Python-2.7.9.tgz; \
cd Python-2.7.9; \
./configure --enable-shared; \
make; \
make install; \
cd ..; \
rm -rf /Python-2.7.9; \
rm Python-2.7.9.tgz
# Make libpython findable
ENV LD_LIBRARY_PATH /usr/local/lib
# Install setuptools
RUN set -ex; \
curl -LO https://bootstrap.pypa.io/ez_setup.py; \
python ez_setup.py; \
rm ez_setup.py
# Install pip
RUN set -ex; \
curl -LO https://pypi.python.org/packages/source/p/pip/pip-7.0.1.tar.gz; \
tar -xzf pip-7.0.1.tar.gz; \
cd pip-7.0.1; \
python setup.py install; \
cd ..; \
rm -rf pip-7.0.1; \
rm pip-7.0.1.tar.gz
ENV ALL_DOCKER_VERSIONS 1.7.1 1.8.0-rc3
RUN set -ex; \
for v in ${ALL_DOCKER_VERSIONS}; do \
curl https://get.docker.com/builds/Linux/x86_64/docker-$v -o /usr/local/bin/docker-$v; \
chmod +x /usr/local/bin/docker-$v; \
done
curl https://get.docker.com/builds/Linux/x86_64/docker-1.7.1 -o /usr/local/bin/docker-1.7.1; \
chmod +x /usr/local/bin/docker-1.7.1; \
curl https://test.docker.com/builds/Linux/x86_64/docker-1.8.0-rc3 -o /usr/local/bin/docker-1.8.0-rc3; \
chmod +x /usr/local/bin/docker-1.8.0-rc3
# Set the default Docker to be run
RUN ln -s /usr/local/bin/docker-1.3.3 /usr/local/bin/docker
RUN ln -s /usr/local/bin/docker-1.7.1 /usr/local/bin/docker
RUN useradd -d /home/user -m -s /bin/bash user
WORKDIR /code/

View File

@@ -1,3 +1,4 @@
Aanand Prasad <aanand.prasad@gmail.com> (@aanand)
Ben Firshman <ben@firshman.co.uk> (@bfirsh)
Daniel Nephin <dnephin@gmail.com> (@dnephin)
Mazz Mosley <mazz@houseofmnowster.com> (@mnowster)

View File

@@ -4,7 +4,7 @@ include requirements.txt
include requirements-dev.txt
include tox.ini
include *.md
include contrib/completion/bash/docker-compose
recursive-include contrib/completion *
recursive-include tests *
global-exclude *.pyc
global-exclude *.pyo

View File

@@ -1,45 +1,35 @@
Docker Compose
==============
[![Build Status](http://jenkins.dockerproject.com/buildStatus/icon?job=Compose Master)](http://jenkins.dockerproject.com/job/Compose%20Master/)
*(Previously known as Fig)*
Compose is a tool for defining and running complex applications with Docker.
With Compose, you define a multi-container application in a single file, then
spin your application up in a single command which does everything that needs to
be done to get it running.
Compose is a tool for defining and running multi-container applications with
Docker. With Compose, you define a multi-container application in a single
file, then spin your application up in a single command which does everything
that needs to be done to get it running.
Compose is great for development environments, staging servers, and CI. We don't
recommend that you use it in production yet.
Using Compose is basically a three-step process.
First, you define your app's environment with a `Dockerfile` so it can be
reproduced anywhere:
```Dockerfile
FROM python:2.7
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code
CMD python app.py
```
Next, you define the services that make up your app in `docker-compose.yml` so
1. Define your app's environment with a `Dockerfile` so it can be
reproduced anywhere.
2. Define the services that make up your app in `docker-compose.yml` so
they can be run together in an isolated environment:
3. Lastly, run `docker-compose up` and Compose will start and run your entire app.
```yaml
web:
build: .
links:
- db
ports:
- "8000:8000"
db:
image: postgres
```
A `docker-compose.yml` looks like this:
Lastly, run `docker-compose up` and Compose will start and run your entire app.
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
Compose has commands for managing the whole lifecycle of your application:
@@ -52,4 +42,16 @@ Installation and documentation
------------------------------
- Full documentation is available on [Docker's website](http://docs.docker.com/compose/).
- Hop into #docker-compose on Freenode if you have any questions.
- If you have any questions, you can talk in real-time with other developers in the #docker-compose IRC channel on Freenode. [Click here to join using IRCCloud.](https://www.irccloud.com/invite?hostname=irc.freenode.net&channel=%23docker-compose)
Contributing
------------
[![Build Status](http://jenkins.dockerproject.org/buildStatus/icon?job=Compose%20Master)](http://jenkins.dockerproject.org/job/Compose%20Master/)
Want to help build Compose? Check out our [contributing documentation](https://github.com/docker/compose/blob/master/CONTRIBUTING.md).
Releasing
---------
Releases are built by maintainers, following an outline of the [release process](https://github.com/docker/compose/blob/master/RELEASE_PROCESS.md).

36
RELEASE_PROCESS.md Normal file
View File

@@ -0,0 +1,36 @@
# Building a Compose release
## Building binaries
`script/build-linux` builds the Linux binary inside a Docker container:
$ script/build-linux
`script/build-osx` builds the Mac OS X binary inside a virtualenv:
$ script/build-osx
For official releases, you should build inside a Mountain Lion VM for proper
compatibility. Run the this script first to prepare the environment before
building - it will use Homebrew to make sure Python is installed and
up-to-date.
$ script/prepare-osx
## Release process
1. Open pull request that:
- Updates the version in `compose/__init__.py`
- Updates the binary URL in `docs/install.md`
- Adds release notes to `CHANGES.md`
2. Create unpublished GitHub release with release notes
3. Build Linux version on any Docker host with `script/build-linux` and attach
to release
4. Build OS X version on Mountain Lion with `script/build-osx` and attach to
release as `docker-compose-Darwin-x86_64` and `docker-compose-Linux-x86_64`.
5. Publish GitHub release, creating tag
6. Update website with `script/deploy-docs`
7. Upload PyPi package
$ git checkout $VERSION
$ python setup.py sdist upload

View File

@@ -4,9 +4,12 @@
Over time we will extend Compose's remit to cover test, staging and production environments. This is not a simple task, and will take many incremental improvements such as:
- Composes brute-force “delete and recreate everything” approach is great for dev and testing, but it not sufficient for production environments. You should be able to define a "desired" state that Compose will intelligently converge to.
- It should be possible to partially modify the config file for different environments (dev/test/staging/prod), passing in e.g. custom ports or volume mount paths. ([#426](https://github.com/docker/fig/issues/426))
- Compose currently will attempt to get your application into the correct state when running `up`, but it has a number of shortcomings:
- It should roll back to a known good state if it fails.
- It should allow a user to check the actions it is about to perform before running them.
- It should be possible to partially modify the config file for different environments (dev/test/staging/prod), passing in e.g. custom ports or volume mount paths. ([#1377](https://github.com/docker/compose/issues/1377))
- Compose should recommend a technique for zero-downtime deploys.
- It should be possible to continuously attempt to keep an application in the correct state, instead of just performing `up` a single time.
## Integration with Swarm

View File

@@ -3,49 +3,37 @@ Docker Compose/Swarm integration
Eventually, Compose and Swarm aim to have full integration, meaning you can point a Compose app at a Swarm cluster and have it all just work as if you were using a single Docker host.
However, the current extent of integration is minimal: Compose can create containers on a Swarm cluster, but the majority of Compose apps wont work out of the box unless all containers are scheduled on one host, defeating much of the purpose of using Swarm in the first place.
However, integration is currently incomplete: Compose can create containers on a Swarm cluster, but the majority of Compose apps wont work out of the box unless all containers are scheduled on one host, because links between containers do not work across hosts.
Still, Compose and Swarm can be useful in a “batch processing” scenario (where a large number of containers need to be spun up and down to do independent computation) or a “shared cluster” scenario (where multiple teams want to deploy apps on a cluster without worrying about where to put them).
Docker networking is [getting overhauled](https://github.com/docker/libnetwork) in such a way that itll fit the multi-host model much better. For now, linked containers are automatically scheduled on the same host.
A number of things need to happen before full integration is achieved, which are documented below.
Building
--------
Re-deploying containers with `docker-compose up`
------------------------------------------------
Swarm can build an image from a Dockerfile just like a single-host Docker instance can, but the resulting image will only live on a single node and won't be distributed to other nodes.
Repeated invocations of `docker-compose up` will not work reliably when used against a Swarm cluster because of an under-the-hood design problem; [this will be fixed](https://github.com/docker/fig/pull/972) in the next version of Compose. For now, containers must be completely removed and re-created:
If you want to use Compose to scale the service in question to multiple nodes, you'll have to build it yourself, push it to a registry (e.g. the Docker Hub) and reference it from `docker-compose.yml`:
$ docker-compose kill
$ docker-compose rm --force
$ docker-compose up
$ docker build -t myusername/web .
$ docker push myusername/web
Links and networking
--------------------
$ cat docker-compose.yml
web:
image: myusername/web
The primary thing stopping multi-container apps from working seamlessly on Swarm is getting them to talk to one another: enabling private communication between containers on different hosts hasnt been solved in a non-hacky way.
$ docker-compose up -d
$ docker-compose scale web=3
Long-term, networking is [getting overhauled](https://github.com/docker/docker/issues/9983) in such a way that itll fit the multi-host model much better. For now, containers on different hosts cannot be linked. In the next version of Compose, linked services will be automatically scheduled on the same host; for now, this must be done manually (see “Co-scheduling containers” below).
Scheduling
----------
`volumes_from` and `net: container`
-----------------------------------
Swarm offers a rich set of scheduling and affinity hints, enabling you to control where containers are located. They are specified via container environment variables, so you can use Compose's `environment` option to set them.
For containers to share volumes or a network namespace, they must be scheduled on the same host - this is, after all, inherent to how both volumes and network namespaces work. In the next version of Compose, this co-scheduling will be automatic whenever `volumes_from` or `net: "container:..."` is specified; for now, containers which share volumes or a network namespace must be co-scheduled manually (see “Co-scheduling containers” below).
environment:
# Schedule containers on a node that has the 'storage' label set to 'ssd'
- "constraint:storage==ssd"
Co-scheduling containers
------------------------
# Schedule containers where the 'redis' image is already pulled
- "affinity:image==redis"
For now, containers can be manually scheduled on the same host using Swarms [affinity filters](https://github.com/docker/swarm/blob/master/scheduler/filter/README.md#affinity-filter). Heres a simple example:
```yaml
web:
image: my-web-image
links: ["db"]
environment:
- "affinity:container==myproject_db_*"
db:
image: postgres
```
Here, we express an affinity filter on all web containers, saying that each one must run alongside a container whose name begins with `myproject_db_`.
- `myproject` is the common prefix Compose gives to all containers in your project, which is either generated from the name of the current directory or specified with `-p` or the `DOCKER_COMPOSE_PROJECT_NAME` environment variable.
- `*` is a wildcard, which works just like filename wildcards in a Unix shell.
For the full set of available filters and expressions, see the [Swarm documentation](https://docs.docker.com/swarm/scheduler/filter/).

View File

@@ -1,4 +1,3 @@
from __future__ import unicode_literals
from .service import Service # noqa:flake8
__version__ = '1.2.0'
__version__ = '1.4.1'

View File

@@ -41,7 +41,7 @@ class Command(DocoptCommand):
raise errors.ConnectionErrorGeneric(self.get_client().base_url)
def perform_command(self, options, handler, command_options):
if options['COMMAND'] == 'help':
if options['COMMAND'] in ('help', 'version'):
# Skip looking up the compose file.
handler(None, command_options)
return
@@ -52,7 +52,7 @@ class Command(DocoptCommand):
explicit_config_path = options.get('--file') or os.environ.get('COMPOSE_FILE') or os.environ.get('FIG_FILE')
project = self.get_project(
self.get_config_path(explicit_config_path),
explicit_config_path,
project_name=options.get('--project-name'),
verbose=options.get('--verbose'))
@@ -69,16 +69,18 @@ class Command(DocoptCommand):
return verbose_proxy.VerboseProxy('docker', client)
return client
def get_project(self, config_path, project_name=None, verbose=False):
def get_project(self, config_path=None, project_name=None, verbose=False):
config_details = config.find(self.base_dir, config_path)
try:
return Project.from_dicts(
self.get_project_name(config_path, project_name),
config.load(config_path),
self.get_project_name(config_details.working_dir, project_name),
config.load(config_details),
self.get_client(verbose=verbose))
except ConfigError as e:
raise errors.UserError(six.text_type(e))
def get_project_name(self, config_path, project_name=None):
def get_project_name(self, working_dir, project_name=None):
def normalize_name(name):
return re.sub(r'[^a-z0-9]', '', name.lower())
@@ -86,48 +88,15 @@ class Command(DocoptCommand):
log.warn('The FIG_PROJECT_NAME environment variable is deprecated.')
log.warn('Please use COMPOSE_PROJECT_NAME instead.')
project_name = project_name or os.environ.get('COMPOSE_PROJECT_NAME') or os.environ.get('FIG_PROJECT_NAME')
project_name = (
project_name or
os.environ.get('COMPOSE_PROJECT_NAME') or
os.environ.get('FIG_PROJECT_NAME'))
if project_name is not None:
return normalize_name(project_name)
project = os.path.basename(os.path.dirname(os.path.abspath(config_path)))
project = os.path.basename(os.path.abspath(working_dir))
if project:
return normalize_name(project)
return 'default'
def get_config_path(self, file_path=None):
if file_path:
return os.path.join(self.base_dir, file_path)
supported_filenames = [
'docker-compose.yml',
'docker-compose.yaml',
'fig.yml',
'fig.yaml',
]
def expand(filename):
return os.path.join(self.base_dir, filename)
candidates = [filename for filename in supported_filenames if os.path.exists(expand(filename))]
if len(candidates) == 0:
raise errors.ComposeFileNotFound(supported_filenames)
winner = candidates[0]
if len(candidates) > 1:
log.warning("Found multiple config files with supported names: %s", ", ".join(candidates))
log.warning("Using %s\n", winner)
if winner == 'docker-compose.yaml':
log.warning("Please be aware that .yml is the expected extension "
"in most cases, and using .yaml can cause compatibility "
"issues in future.\n")
if winner.startswith("fig."):
log.warning("%s is deprecated and will not be supported in future. "
"Please rename your config file to docker-compose.yml\n" % winner)
return expand(winner)

View File

@@ -14,6 +14,8 @@ def docker_client():
cert_path = os.path.join(os.environ.get('HOME', ''), '.docker')
base_url = os.environ.get('DOCKER_HOST')
api_version = os.environ.get('COMPOSE_API_VERSION', '1.19')
tls_config = None
if os.environ.get('DOCKER_TLS_VERIFY', '') != '':
@@ -32,4 +34,4 @@ def docker_client():
)
timeout = int(os.environ.get('DOCKER_CLIENT_TIMEOUT', 60))
return Client(base_url=base_url, tls=tls_config, version='1.15', timeout=timeout)
return Client(base_url=base_url, tls=tls_config, version=api_version, timeout=timeout)

View File

@@ -33,10 +33,7 @@ class DocoptCommand(object):
if command is None:
raise SystemExit(getdoc(self))
if not hasattr(self, command):
raise NoSuchCommand(command, self)
handler = getattr(self, command)
handler = self.get_handler(command)
docstring = getdoc(handler)
if docstring is None:
@@ -45,6 +42,14 @@ class DocoptCommand(object):
command_options = docopt_full_help(docstring, options['ARGS'], options_first=True)
return options, handler, command_options
def get_handler(self, command):
command = command.replace('-', '_')
if not hasattr(self, command):
raise NoSuchCommand(command, self)
return getattr(self, command)
class NoSuchCommand(Exception):
def __init__(self, command, supercommand):

View File

@@ -53,12 +53,3 @@ class ConnectionErrorGeneric(UserError):
If it's at a non-standard location, specify the URL with the DOCKER_HOST environment variable.
""" % url)
class ComposeFileNotFound(UserError):
def __init__(self, supported_filenames):
super(ComposeFileNotFound, self).__init__("""
Can't find a suitable configuration file. Are you in the right directory?
Supported filenames: %s
""" % ", ".join(supported_filenames))

View File

@@ -7,7 +7,7 @@ import texttable
def get_tty_width():
tty_size = os.popen('stty size', 'r').read().split()
if len(tty_size) != 2:
return 80
return 0
_, width = tty_size
return int(width)

View File

@@ -11,18 +11,26 @@ from docker.errors import APIError
import dockerpty
from .. import __version__
from .. import legacy
from ..const import DEFAULT_TIMEOUT
from ..project import NoSuchService, ConfigurationError
from ..service import BuildError, CannotBeScaledError
from ..service import BuildError, NeedsBuildError
from ..config import parse_environment
from ..progress_stream import StreamOutputError
from .command import Command
from .docopt_command import NoSuchCommand
from .errors import UserError
from .formatter import Formatter
from .log_printer import LogPrinter
from .utils import yesno
from .utils import yesno, get_version_info
log = logging.getLogger(__name__)
INSECURE_SSL_WARNING = """
Warning: --allow-insecure-ssl is deprecated and has no effect.
It will be removed in a future version of Compose.
"""
def main():
setup_logging()
@@ -32,7 +40,7 @@ def main():
except KeyboardInterrupt:
log.error("\nAborting.")
sys.exit(1)
except (UserError, NoSuchService, ConfigurationError) as e:
except (UserError, NoSuchService, ConfigurationError, legacy.LegacyError) as e:
log.error(e.msg)
sys.exit(1)
except NoSuchCommand as e:
@@ -46,6 +54,12 @@ def main():
except BuildError as e:
log.error("Service '%s' failed to build: %s" % (e.service.name, e.reason))
sys.exit(1)
except StreamOutputError as e:
log.error(e)
sys.exit(1)
except NeedsBuildError as e:
log.error("Service '%s' needs to be built, but --no-build was passed." % e.service.name)
sys.exit(1)
def setup_logging():
@@ -68,38 +82,40 @@ def parse_doc_section(name, source):
class TopLevelCommand(Command):
"""Fast, isolated development environments using Docker.
"""Define and run multi-container applications with Docker.
Usage:
docker-compose [options] [COMMAND] [ARGS...]
docker-compose -h|--help
Options:
--verbose Show more output
--version Print version and exit
-f, --file FILE Specify an alternate compose file (default: docker-compose.yml)
-p, --project-name NAME Specify an alternate project name (default: directory name)
--verbose Show more output
-v, --version Print version and exit
Commands:
build Build or rebuild services
help Get help on a command
kill Kill containers
logs View output from containers
port Print the public port for a port binding
ps List containers
pull Pulls service images
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
restart Restart services
up Create and start containers
build Build or rebuild services
help Get help on a command
kill Kill containers
logs View output from containers
port Print the public port for a port binding
ps List containers
pull Pulls service images
restart Restart services
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
up Create and start containers
migrate-to-labels Recreate containers to add labels
version Show the Docker-Compose version information
"""
def docopt_options(self):
options = super(TopLevelCommand, self).docopt_options()
options['version'] = "docker-compose %s" % __version__
options['version'] = get_version_info('compose')
return options
def build(self, project, options):
@@ -108,7 +124,7 @@ class TopLevelCommand(Command):
Services are built once and then tagged as `project_service`,
e.g. `composetest_db`. If you change a service's `Dockerfile` or the
contents of its build directory, you can run `compose build` to rebuild it.
contents of its build directory, you can run `docker-compose build` to rebuild it.
Usage: build [options] [SERVICE...]
@@ -124,10 +140,8 @@ class TopLevelCommand(Command):
Usage: help COMMAND
"""
command = options['COMMAND']
if not hasattr(self, command):
raise NoSuchCommand(command, self)
raise SystemExit(getdoc(getattr(self, command)))
handler = self.get_handler(options['COMMAND'])
raise SystemExit(getdoc(handler))
def kill(self, project, options):
"""
@@ -165,13 +179,14 @@ class TopLevelCommand(Command):
Usage: port [options] SERVICE PRIVATE_PORT
Options:
--protocol=proto tcp or udp (defaults to tcp)
--protocol=proto tcp or udp [default: tcp]
--index=index index of the container if there are multiple
instances of a service (defaults to 1)
instances of a service [default: 1]
"""
index = int(options.get('--index'))
service = project.get_service(options['SERVICE'])
try:
container = service.get_container(number=options.get('--index') or 1)
container = service.get_container(number=index)
except ValueError as e:
raise UserError(str(e))
print(container.get_local_port(
@@ -222,13 +237,13 @@ class TopLevelCommand(Command):
Usage: pull [options] [SERVICE...]
Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
--allow-insecure-ssl Deprecated - no effect.
"""
insecure_registry = options['--allow-insecure-ssl']
if options['--allow-insecure-ssl']:
log.warn(INSECURE_SSL_WARNING)
project.pull(
service_names=options['SERVICE'],
insecure_registry=insecure_registry
)
def rm(self, project, options):
@@ -270,8 +285,7 @@ class TopLevelCommand(Command):
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]
Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
--allow-insecure-ssl Deprecated - no effect.
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
@@ -286,18 +300,17 @@ class TopLevelCommand(Command):
"""
service = project.get_service(options['SERVICE'])
insecure_registry = options['--allow-insecure-ssl']
if options['--allow-insecure-ssl']:
log.warn(INSECURE_SSL_WARNING)
if not options['--no-deps']:
deps = service.get_linked_names()
deps = service.get_linked_service_names()
if len(deps) > 0:
project.up(
service_names=deps,
start_deps=True,
recreate=False,
insecure_registry=insecure_registry,
detach=options['-d']
allow_recreate=False,
)
tty = True
@@ -317,35 +330,43 @@ class TopLevelCommand(Command):
}
if options['-e']:
# Merge environment from config with -e command line
container_options['environment'] = dict(
parse_environment(service.options.get('environment')),
**parse_environment(options['-e']))
container_options['environment'] = parse_environment(options['-e'])
if options['--entrypoint']:
container_options['entrypoint'] = options.get('--entrypoint')
if options['--rm']:
container_options['restart'] = None
if options['--user']:
container_options['user'] = options.get('--user')
if not options['--service-ports']:
container_options['ports'] = []
container = service.create_container(
one_off=True,
insecure_registry=insecure_registry,
**container_options
)
try:
container = service.create_container(
quiet=True,
one_off=True,
**container_options
)
except APIError as e:
legacy.check_for_legacy_containers(
project.client,
project.name,
[service.name],
allow_one_off=False,
)
raise e
if options['-d']:
service.start_container(container)
print(container.name)
else:
service.start_container(container)
dockerpty.start(project.client, container.id, interactive=not options['-T'])
exit_code = container.wait()
if options['--rm']:
log.info("Removing %s..." % container.name)
project.client.remove_container(container.id)
sys.exit(exit_code)
@@ -358,8 +379,14 @@ class TopLevelCommand(Command):
$ docker-compose scale web=2 worker=3
Usage: scale [SERVICE=NUM...]
Usage: scale [options] [SERVICE=NUM...]
Options:
-t, --timeout TIMEOUT Specify a shutdown timeout in seconds.
(default: 10)
"""
timeout = int(options.get('--timeout') or DEFAULT_TIMEOUT)
for s in options['SERVICE=NUM']:
if '=' not in s:
raise UserError('Arguments to scale should be in the form service=num')
@@ -369,15 +396,7 @@ class TopLevelCommand(Command):
except ValueError:
raise UserError('Number of containers for service "%s" is not a '
'number' % service_name)
try:
project.get_service(service_name).scale(num)
except CannotBeScaledError:
raise UserError(
'Service "%s" cannot be scaled because it specifies a port '
'on the host. If multiple containers for this service were '
'created, the port would clash.\n\nRemove the ":" from the '
'port definition in docker-compose.yml so Docker can choose a random '
'port for each container.' % service_name)
project.get_service(service_name).scale(num, timeout=timeout)
def start(self, project, options):
"""
@@ -399,9 +418,8 @@ class TopLevelCommand(Command):
-t, --timeout TIMEOUT Specify a shutdown timeout in seconds.
(default: 10)
"""
timeout = options.get('--timeout')
params = {} if timeout is None else {'timeout': int(timeout)}
project.stop(service_names=options['SERVICE'], **params)
timeout = int(options.get('--timeout') or DEFAULT_TIMEOUT)
project.stop(service_names=options['SERVICE'], timeout=timeout)
def restart(self, project, options):
"""
@@ -413,75 +431,134 @@ class TopLevelCommand(Command):
-t, --timeout TIMEOUT Specify a shutdown timeout in seconds.
(default: 10)
"""
timeout = options.get('--timeout')
params = {} if timeout is None else {'timeout': int(timeout)}
project.restart(service_names=options['SERVICE'], **params)
timeout = int(options.get('--timeout') or DEFAULT_TIMEOUT)
project.restart(service_names=options['SERVICE'], timeout=timeout)
def up(self, project, options):
"""
Build, (re)create, start and attach to containers for a service.
Builds, (re)creates, starts, and attaches to containers for a service.
By default, `docker-compose up` will aggregate the output of each container, and
when it exits, all containers will be stopped. If you run `docker-compose up -d`,
it'll start the containers in the background and leave them running.
Unless they are already running, this command also starts any linked services.
If there are existing containers for a service, `docker-compose up` will stop
and recreate them (preserving mounted volumes with volumes-from),
so that changes in `docker-compose.yml` are picked up. If you do not want existing
containers to be recreated, `docker-compose up --no-recreate` will re-use existing
containers.
The `docker-compose up` command aggregates the output of each container. When
the command exits, all containers are stopped. Running `docker-compose up -d`
starts the containers in the background and leaves them running.
If there are existing containers for a service, and the service's configuration
or image was changed after the container's creation, `docker-compose up` picks
up the changes by stopping and recreating the containers (preserving mounted
volumes). To prevent Compose from picking up changes, use the `--no-recreate`
flag.
If you want to force Compose to stop and recreate all containers, use the
`--force-recreate` flag.
Usage: up [options] [SERVICE...]
Options:
--allow-insecure-ssl Allow insecure connections to the docker
registry
--allow-insecure-ssl Deprecated - no effect.
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--force-recreate Recreate containers even if their configuration and
image haven't changed. Incompatible with --no-recreate.
--no-recreate If containers already exist, don't recreate them.
Incompatible with --force-recreate.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT When attached, use this timeout in seconds
for the shutdown. (default: 10)
-t, --timeout TIMEOUT Use this timeout in seconds for container shutdown
when attached or when containers are already
running. (default: 10)
"""
insecure_registry = options['--allow-insecure-ssl']
if options['--allow-insecure-ssl']:
log.warn(INSECURE_SSL_WARNING)
detached = options['-d']
monochrome = options['--no-color']
start_deps = not options['--no-deps']
recreate = not options['--no-recreate']
allow_recreate = not options['--no-recreate']
force_recreate = options['--force-recreate']
service_names = options['SERVICE']
timeout = int(options.get('--timeout') or DEFAULT_TIMEOUT)
project.up(
if force_recreate and not allow_recreate:
raise UserError("--force-recreate and --no-recreate cannot be combined.")
to_attach = project.up(
service_names=service_names,
start_deps=start_deps,
recreate=recreate,
insecure_registry=insecure_registry,
detach=detached,
allow_recreate=allow_recreate,
force_recreate=force_recreate,
do_build=not options['--no-build'],
timeout=timeout
)
to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
if not detached:
print("Attaching to", list_containers(to_attach))
log_printer = LogPrinter(to_attach, attach_params={"logs": True}, monochrome=monochrome)
log_printer = build_log_printer(to_attach, service_names, monochrome)
attach_to_logs(project, log_printer, service_names, timeout)
try:
log_printer.run()
finally:
def handler(signal, frame):
project.kill(service_names=service_names)
sys.exit(0)
signal.signal(signal.SIGINT, handler)
def migrate_to_labels(self, project, _options):
"""
Recreate containers to add labels
print("Gracefully stopping... (press Ctrl+C again to force)")
timeout = options.get('--timeout')
params = {} if timeout is None else {'timeout': int(timeout)}
project.stop(service_names=service_names, **params)
If you're coming from Compose 1.2 or earlier, you'll need to remove or
migrate your existing containers after upgrading Compose. This is
because, as of version 1.3, Compose uses Docker labels to keep track
of containers, and so they need to be recreated with labels added.
If Compose detects containers that were created without labels, it
will refuse to run so that you don't end up with two sets of them. If
you want to keep using your existing containers (for example, because
they have data volumes you want to preserve) you can migrate them with
the following command:
docker-compose migrate-to-labels
Alternatively, if you're not worried about keeping them, you can
remove them - Compose will just create new ones.
docker rm -f myapp_web_1 myapp_db_1 ...
Usage: migrate-to-labels
"""
legacy.migrate_project_to_labels(project)
def version(self, project, options):
"""
Show version informations
Usage: version [--short]
Options:
--short Shows only Compose's version number.
"""
if options['--short']:
print(__version__)
else:
print(get_version_info('full'))
def build_log_printer(containers, service_names, monochrome):
return LogPrinter(
[c for c in containers if c.service in service_names],
attach_params={"logs": True},
monochrome=monochrome)
def attach_to_logs(project, log_printer, service_names, timeout):
print("Attaching to", list_containers(log_printer.containers))
try:
log_printer.run()
finally:
def handler(signal, frame):
project.kill(service_names=service_names)
sys.exit(0)
signal.signal(signal.SIGINT, handler)
print("Gracefully stopping... (press Ctrl+C again to force)")
project.stop(service_names=service_names, timeout=timeout)
def list_containers(containers):

View File

@@ -1,10 +1,14 @@
from __future__ import unicode_literals
from __future__ import absolute_import
from __future__ import division
from .. import __version__
import datetime
from docker import version as docker_py_version
import os
import subprocess
import platform
import subprocess
import ssl
def yesno(prompt, default=None):
@@ -62,6 +66,25 @@ def mkdir(path, permissions=0o700):
return path
def find_candidates_in_parent_dirs(filenames, path):
"""
Given a directory path to start, looks for filenames in the
directory, and then each parent directory successively,
until found.
Returns tuple (candidates, path).
"""
candidates = [filename for filename in filenames
if os.path.exists(os.path.join(path, filename))]
if len(candidates) == 0:
parent_dir = os.path.join(path, '..')
if os.path.abspath(parent_dir) != os.path.abspath(path):
return find_candidates_in_parent_dirs(filenames, parent_dir)
return (candidates, path)
def split_buffer(reader, separator):
"""
Given a generator which yields strings and a separator string,
@@ -101,3 +124,16 @@ def is_mac():
def is_ubuntu():
return platform.system() == 'Linux' and platform.linux_distribution()[0] == 'Ubuntu'
def get_version_info(scope):
versioninfo = 'docker-compose version: %s' % __version__
if scope == 'compose':
return versioninfo
elif scope == 'full':
return versioninfo + '\n' \
+ "docker-py version: %s\n" % docker_py_version \
+ "%s version: %s\n" % (platform.python_implementation(), platform.python_version()) \
+ "OpenSSL version: %s" % ssl.OPENSSL_VERSION
else:
raise RuntimeError('passed unallowed value to `cli.utils.get_version_info`')

View File

@@ -1,31 +1,49 @@
import logging
import os
import sys
import yaml
from collections import namedtuple
import six
from compose.cli.utils import find_candidates_in_parent_dirs
DOCKER_CONFIG_KEYS = [
'cap_add',
'cap_drop',
'cpu_shares',
'command',
'cpu_shares',
'cpuset',
'detach',
'devices',
'dns',
'dns_search',
'domainname',
'entrypoint',
'env_file',
'environment',
'extra_hosts',
'hostname',
'image',
'labels',
'links',
'log_driver',
'log_opt',
'mac_address',
'mem_limit',
'memswap_limit',
'net',
'pid',
'ports',
'privileged',
'read_only',
'restart',
'security_opt',
'stdin_open',
'tty',
'user',
'volume_driver',
'volumes',
'volumes_from',
'working_dir',
@@ -33,29 +51,88 @@ DOCKER_CONFIG_KEYS = [
ALLOWED_KEYS = DOCKER_CONFIG_KEYS + [
'build',
'container_name',
'dockerfile',
'expose',
'external_links',
'name',
]
DOCKER_CONFIG_HINTS = {
'cpu_share' : 'cpu_shares',
'link' : 'links',
'port' : 'ports',
'privilege' : 'privileged',
'cpu_share': 'cpu_shares',
'add_host': 'extra_hosts',
'hosts': 'extra_hosts',
'extra_host': 'extra_hosts',
'device': 'devices',
'link': 'links',
'memory_swap': 'memswap_limit',
'port': 'ports',
'privilege': 'privileged',
'priviliged': 'privileged',
'privilige' : 'privileged',
'volume' : 'volumes',
'workdir' : 'working_dir',
'privilige': 'privileged',
'volume': 'volumes',
'workdir': 'working_dir',
}
def load(filename):
working_dir = os.path.dirname(filename)
return from_dictionary(load_yaml(filename), working_dir=working_dir, filename=filename)
SUPPORTED_FILENAMES = [
'docker-compose.yml',
'docker-compose.yaml',
'fig.yml',
'fig.yaml',
]
def from_dictionary(dictionary, working_dir=None, filename=None):
PATH_START_CHARS = [
'/',
'.',
'~',
]
log = logging.getLogger(__name__)
ConfigDetails = namedtuple('ConfigDetails', 'config working_dir filename')
def find(base_dir, filename):
if filename == '-':
return ConfigDetails(yaml.safe_load(sys.stdin), os.getcwd(), None)
if filename:
filename = os.path.join(base_dir, filename)
else:
filename = get_config_path(base_dir)
return ConfigDetails(load_yaml(filename), os.path.dirname(filename), filename)
def get_config_path(base_dir):
(candidates, path) = find_candidates_in_parent_dirs(SUPPORTED_FILENAMES, base_dir)
if len(candidates) == 0:
raise ComposeFileNotFound(SUPPORTED_FILENAMES)
winner = candidates[0]
if len(candidates) > 1:
log.warn("Found multiple config files with supported names: %s", ", ".join(candidates))
log.warn("Using %s\n", winner)
if winner == 'docker-compose.yaml':
log.warn("Please be aware that .yml is the expected extension "
"in most cases, and using .yaml can cause compatibility "
"issues in future.\n")
if winner.startswith("fig."):
log.warn("%s is deprecated and will not be supported in future. "
"Please rename your config file to docker-compose.yml\n" % winner)
return os.path.join(path, winner)
def load(config_details):
dictionary, working_dir, filename = config_details
service_dicts = []
for service_name, service_dict in list(dictionary.items()):
@@ -63,25 +140,26 @@ def from_dictionary(dictionary, working_dir=None, filename=None):
raise ConfigurationError('Service "%s" doesn\'t have any configuration options. All top level keys in your docker-compose.yml must map to a dictionary of configuration options.' % service_name)
loader = ServiceLoader(working_dir=working_dir, filename=filename)
service_dict = loader.make_service_dict(service_name, service_dict)
validate_paths(service_dict)
service_dicts.append(service_dict)
return service_dicts
def make_service_dict(name, service_dict, working_dir=None):
return ServiceLoader(working_dir=working_dir).make_service_dict(name, service_dict)
class ServiceLoader(object):
def __init__(self, working_dir, filename=None, already_seen=None):
self.working_dir = working_dir
self.filename = filename
self.working_dir = os.path.abspath(working_dir)
if filename:
self.filename = os.path.abspath(filename)
else:
self.filename = filename
self.already_seen = already_seen or []
def make_service_dict(self, name, service_dict):
def detect_cycle(self, name):
if self.signature(name) in self.already_seen:
raise CircularReference(self.already_seen)
raise CircularReference(self.already_seen + [self.signature(name)])
def make_service_dict(self, name, service_dict):
service_dict = service_dict.copy()
service_dict['name'] = name
service_dict = resolve_environment(service_dict, working_dir=self.working_dir)
@@ -92,12 +170,17 @@ class ServiceLoader(object):
if 'extends' not in service_dict:
return service_dict
extends_options = process_extends_options(service_dict['name'], service_dict['extends'])
extends_options = self.validate_extends_options(service_dict['name'], service_dict['extends'])
if self.working_dir is None:
raise Exception("No working_dir passed to ServiceLoader()")
other_config_path = expand_path(self.working_dir, extends_options['file'])
if 'file' in extends_options:
extends_from_filename = extends_options['file']
other_config_path = expand_path(self.working_dir, extends_from_filename)
else:
other_config_path = self.filename
other_working_dir = os.path.dirname(other_config_path)
other_already_seen = self.already_seen + [self.signature(service_dict['name'])]
other_loader = ServiceLoader(
@@ -108,6 +191,7 @@ class ServiceLoader(object):
other_config = load_yaml(other_config_path)
other_service_dict = other_config[extends_options['service']]
other_loader.detect_cycle(extends_options['service'])
other_service_dict = other_loader.make_service_dict(
service_dict['name'],
other_service_dict,
@@ -123,25 +207,29 @@ class ServiceLoader(object):
def signature(self, name):
return (self.filename, name)
def validate_extends_options(self, service_name, extends_options):
error_prefix = "Invalid 'extends' configuration for %s:" % service_name
def process_extends_options(service_name, extends_options):
error_prefix = "Invalid 'extends' configuration for %s:" % service_name
if not isinstance(extends_options, dict):
raise ConfigurationError("%s must be a dictionary" % error_prefix)
if not isinstance(extends_options, dict):
raise ConfigurationError("%s must be a dictionary" % error_prefix)
if 'service' not in extends_options:
raise ConfigurationError(
"%s you need to specify a service, e.g. 'service: web'" % error_prefix
)
for k, _ in extends_options.items():
if k not in ['file', 'service']:
if 'service' not in extends_options:
raise ConfigurationError(
"%s unsupported configuration option '%s'" % (error_prefix, k)
"%s you need to specify a service, e.g. 'service: web'" % error_prefix
)
return extends_options
if 'file' not in extends_options and self.filename is None:
raise ConfigurationError(
"%s you need to specify a 'file', e.g. 'file: something.yml'" % error_prefix
)
for k, _ in extends_options.items():
if k not in ['file', 'service']:
raise ConfigurationError(
"%s unsupported configuration option '%s'" % (error_prefix, k)
)
return extends_options
def validate_extended_service_dict(service_dict, filename, service):
@@ -168,12 +256,18 @@ def process_container_options(service_dict, working_dir=None):
service_dict = service_dict.copy()
if 'volumes' in service_dict:
service_dict['volumes'] = resolve_host_paths(service_dict['volumes'], working_dir=working_dir)
if 'memswap_limit' in service_dict and 'mem_limit' not in service_dict:
raise ConfigurationError("Invalid 'memswap_limit' configuration for %s service: when defining 'memswap_limit' you must set 'mem_limit' as well" % service_dict['name'])
if 'volumes' in service_dict and service_dict.get('volume_driver') is None:
service_dict['volumes'] = resolve_volume_paths(service_dict, working_dir=working_dir)
if 'build' in service_dict:
service_dict['build'] = resolve_build_path(service_dict['build'], working_dir=working_dir)
if 'labels' in service_dict:
service_dict['labels'] = parse_labels(service_dict['labels'])
return service_dict
@@ -186,10 +280,19 @@ def merge_service_dicts(base, override):
override.get('environment'),
)
if 'volumes' in base or 'volumes' in override:
d['volumes'] = merge_volumes(
base.get('volumes'),
override.get('volumes'),
path_mapping_keys = ['volumes', 'devices']
for key in path_mapping_keys:
if key in base or key in override:
d[key] = merge_path_mappings(
base.get(key),
override.get(key),
)
if 'labels' in base or 'labels' in override:
d['labels'] = merge_labels(
base.get('labels'),
override.get('labels'),
)
if 'image' in override and 'build' in d:
@@ -210,7 +313,7 @@ def merge_service_dicts(base, override):
if key in base or key in override:
d[key] = to_list(base.get(key)) + to_list(override.get(key))
already_merged_keys = ['environment', 'volumes'] + list_keys + list_or_string_keys
already_merged_keys = ['environment', 'labels'] + path_mapping_keys + list_keys + list_or_string_keys
for k in set(ALLOWED_KEYS) - set(already_merged_keys):
if k in override:
@@ -279,7 +382,7 @@ def parse_environment(environment):
return dict(split_env(e) for e in environment)
if isinstance(environment, dict):
return environment
return dict(environment)
raise ConfigurationError(
"environment \"%s\" must be a list or mapping," %
@@ -318,18 +421,33 @@ def env_vars_from_file(filename):
return env
def resolve_host_paths(volumes, working_dir=None):
def resolve_volume_paths(service_dict, working_dir=None):
if working_dir is None:
raise Exception("No working_dir passed to resolve_host_paths()")
raise Exception("No working_dir passed to resolve_volume_paths()")
return [resolve_host_path(v, working_dir) for v in volumes]
return [
resolve_volume_path(v, working_dir, service_dict['name'])
for v in service_dict['volumes']
]
def resolve_host_path(volume, working_dir):
container_path, host_path = split_volume(volume)
def resolve_volume_path(volume, working_dir, service_name):
container_path, host_path = split_path_mapping(volume)
container_path = os.path.expanduser(os.path.expandvars(container_path))
if host_path is not None:
host_path = os.path.expanduser(host_path)
host_path = os.path.expandvars(host_path)
host_path = os.path.expanduser(os.path.expandvars(host_path))
if not any(host_path.startswith(c) for c in PATH_START_CHARS):
log.warn(
'Warning: the mapping "{0}:{1}" in the volumes config for '
'service "{2}" is ambiguous. In a future version of Docker, '
'it will designate a "named" volume '
'(see https://github.com/docker/docker/pull/14242). '
'To prevent unexpected behaviour, change it to "./{0}:{1}"'
.format(host_path, container_path, service_name)
)
return "%s:%s" % (expand_path(working_dir, host_path), container_path)
else:
return container_path
@@ -338,32 +456,34 @@ def resolve_host_path(volume, working_dir):
def resolve_build_path(build_path, working_dir=None):
if working_dir is None:
raise Exception("No working_dir passed to resolve_build_path")
_path = expand_path(working_dir, build_path)
if not os.path.exists(_path) or not os.access(_path, os.R_OK):
raise ConfigurationError("build path %s either does not exist or is not accessible." % _path)
else:
return _path
return expand_path(working_dir, build_path)
def merge_volumes(base, override):
d = dict_from_volumes(base)
d.update(dict_from_volumes(override))
return volumes_from_dict(d)
def validate_paths(service_dict):
if 'build' in service_dict:
build_path = service_dict['build']
if not os.path.exists(build_path) or not os.access(build_path, os.R_OK):
raise ConfigurationError("build path %s either does not exist or is not accessible." % build_path)
def dict_from_volumes(volumes):
if volumes:
return dict(split_volume(v) for v in volumes)
def merge_path_mappings(base, override):
d = dict_from_path_mappings(base)
d.update(dict_from_path_mappings(override))
return path_mappings_from_dict(d)
def dict_from_path_mappings(path_mappings):
if path_mappings:
return dict(split_path_mapping(v) for v in path_mappings)
else:
return {}
def volumes_from_dict(d):
return [join_volume(v) for v in d.items()]
def path_mappings_from_dict(d):
return [join_path_mapping(v) for v in d.items()]
def split_volume(string):
def split_path_mapping(string):
if ':' in string:
(host, container) = string.split(':', 1)
return (container, host)
@@ -371,7 +491,7 @@ def split_volume(string):
return (string, None)
def join_volume(pair):
def join_path_mapping(pair):
(container, host) = pair
if host is None:
return container
@@ -379,6 +499,35 @@ def join_volume(pair):
return ":".join((host, container))
def merge_labels(base, override):
labels = parse_labels(base)
labels.update(parse_labels(override))
return labels
def parse_labels(labels):
if not labels:
return {}
if isinstance(labels, list):
return dict(split_label(e) for e in labels)
if isinstance(labels, dict):
return labels
raise ConfigurationError(
"labels \"%s\" must be a list or mapping" %
labels
)
def split_label(label):
if '=' in label:
return label.split('=', 1)
else:
return label, ''
def expand_path(working_dir, path):
return os.path.abspath(os.path.join(working_dir, path))
@@ -430,3 +579,12 @@ class CircularReference(ConfigurationError):
for (filename, service_name) in self.trail
]
return "Circular reference:\n {}".format("\n extends ".join(lines))
class ComposeFileNotFound(ConfigurationError):
def __init__(self, supported_filenames):
super(ComposeFileNotFound, self).__init__("""
Can't find a suitable configuration file in this directory or any parent. Are you in the right directory?
Supported filenames: %s
""" % ", ".join(supported_filenames))

8
compose/const.py Normal file
View File

@@ -0,0 +1,8 @@
DEFAULT_TIMEOUT = 10
LABEL_CONTAINER_NUMBER = 'com.docker.compose.container-number'
LABEL_ONE_OFF = 'com.docker.compose.oneoff'
LABEL_PROJECT = 'com.docker.compose.project'
LABEL_SERVICE = 'com.docker.compose.service'
LABEL_VERSION = 'com.docker.compose.version'
LABEL_CONFIG_HASH = 'com.docker.compose.config-hash'

View File

@@ -4,6 +4,8 @@ from __future__ import absolute_import
import six
from functools import reduce
from .const import LABEL_CONTAINER_NUMBER, LABEL_SERVICE
class Container(object):
"""
@@ -20,10 +22,14 @@ class Container(object):
"""
Construct a container object from the output of GET /containers/json.
"""
name = get_container_name(dictionary)
if name is None:
return None
new_dictionary = {
'Id': dictionary['Id'],
'Image': dictionary['Image'],
'Name': '/' + get_container_name(dictionary),
'Name': '/' + name,
}
return cls(client, new_dictionary, **kwargs)
@@ -44,6 +50,10 @@ class Container(object):
def image(self):
return self.dictionary['Image']
@property
def image_config(self):
return self.client.inspect_image(self.image)
@property
def short_id(self):
return self.id[:10]
@@ -52,16 +62,21 @@ class Container(object):
def name(self):
return self.dictionary['Name'][1:]
@property
def service(self):
return self.labels.get(LABEL_SERVICE)
@property
def name_without_project(self):
return '_'.join(self.dictionary['Name'].split('_')[1:])
return '{0}_{1}'.format(self.service, self.number)
@property
def number(self):
try:
return int(self.name.split('_')[-1])
except ValueError:
return None
number = self.labels.get(LABEL_CONTAINER_NUMBER)
if not number:
raise ValueError("Container {0} does not have a {1} label".format(
self.short_id, LABEL_CONTAINER_NUMBER))
return int(number)
@property
def ports(self):
@@ -79,6 +94,14 @@ class Container(object):
return ', '.join(format_port(*item)
for item in sorted(six.iteritems(self.ports)))
@property
def labels(self):
return self.get('Config.Labels') or {}
@property
def log_config(self):
return self.get('HostConfig.LogConfig') or None
@property
def human_readable_state(self):
if self.is_running:
@@ -126,8 +149,8 @@ class Container(object):
def kill(self, **options):
return self.client.kill(self.id, **options)
def restart(self):
return self.client.restart(self.id)
def restart(self, **options):
return self.client.restart(self.id, **options)
def remove(self, **options):
return self.client.remove_container(self.id, **options)
@@ -147,6 +170,7 @@ class Container(object):
self.has_been_inspected = True
return self.dictionary
# TODO: only used by tests, move to test module
def links(self):
links = []
for container in self.client.containers():
@@ -163,13 +187,16 @@ class Container(object):
return self.client.attach_socket(self.id, **kwargs)
def __repr__(self):
return '<Container: %s>' % self.name
return '<Container: %s (%s)>' % (self.name, self.id[:6])
def __eq__(self, other):
if type(self) != type(other):
return False
return self.id == other.id
def __hash__(self):
return self.id.__hash__()
def get_container_name(container):
if not container.get('Name') and not container.get('Names'):

180
compose/legacy.py Normal file
View File

@@ -0,0 +1,180 @@
import logging
import re
from .const import LABEL_VERSION
from .container import get_container_name, Container
log = logging.getLogger(__name__)
# TODO: remove this section when migrate_project_to_labels is removed
NAME_RE = re.compile(r'^([^_]+)_([^_]+)_(run_)?(\d+)$')
ERROR_MESSAGE_FORMAT = """
Compose found the following containers without labels:
{names_list}
As of Compose 1.3.0, containers are identified with labels instead of naming convention. If you want to continue using these containers, run:
$ docker-compose migrate-to-labels
Alternatively, remove them:
$ docker rm -f {rm_args}
"""
ONE_OFF_ADDENDUM_FORMAT = """
You should also remove your one-off containers:
$ docker rm -f {rm_args}
"""
ONE_OFF_ERROR_MESSAGE_FORMAT = """
Compose found the following containers without labels:
{names_list}
As of Compose 1.3.0, containers are identified with labels instead of naming convention.
Remove them before continuing:
$ docker rm -f {rm_args}
"""
def check_for_legacy_containers(
client,
project,
services,
allow_one_off=True):
"""Check if there are containers named using the old naming convention
and warn the user that those containers may need to be migrated to
using labels, so that compose can find them.
"""
containers = get_legacy_containers(client, project, services, one_off=False)
if containers:
one_off_containers = get_legacy_containers(client, project, services, one_off=True)
raise LegacyContainersError(
[c.name for c in containers],
[c.name for c in one_off_containers],
)
if not allow_one_off:
one_off_containers = get_legacy_containers(client, project, services, one_off=True)
if one_off_containers:
raise LegacyOneOffContainersError(
[c.name for c in one_off_containers],
)
class LegacyError(Exception):
def __unicode__(self):
return self.msg
__str__ = __unicode__
class LegacyContainersError(LegacyError):
def __init__(self, names, one_off_names):
self.names = names
self.one_off_names = one_off_names
self.msg = ERROR_MESSAGE_FORMAT.format(
names_list="\n".join(" {}".format(name) for name in names),
rm_args=" ".join(names),
)
if one_off_names:
self.msg += ONE_OFF_ADDENDUM_FORMAT.format(rm_args=" ".join(one_off_names))
class LegacyOneOffContainersError(LegacyError):
def __init__(self, one_off_names):
self.one_off_names = one_off_names
self.msg = ONE_OFF_ERROR_MESSAGE_FORMAT.format(
names_list="\n".join(" {}".format(name) for name in one_off_names),
rm_args=" ".join(one_off_names),
)
def add_labels(project, container):
project_name, service_name, one_off, number = NAME_RE.match(container.name).groups()
if project_name != project.name or service_name not in project.service_names:
return
service = project.get_service(service_name)
service.recreate_container(container)
def migrate_project_to_labels(project):
log.info("Running migration to labels for project %s", project.name)
containers = get_legacy_containers(
project.client,
project.name,
project.service_names,
one_off=False,
)
for container in containers:
add_labels(project, container)
def get_legacy_containers(
client,
project,
services,
one_off=False):
return list(_get_legacy_containers_iter(
client,
project,
services,
one_off=one_off,
))
def _get_legacy_containers_iter(
client,
project,
services,
one_off=False):
containers = client.containers(all=True)
for service in services:
for container in containers:
if LABEL_VERSION in (container.get('Labels') or {}):
continue
name = get_container_name(container)
if has_container(project, service, name, one_off=one_off):
yield Container.from_ps(client, container)
def has_container(project, service, name, one_off=False):
if not name or not is_valid_name(name, one_off):
return False
container_project, container_service, _container_number = parse_name(name)
return container_project == project and container_service == service
def is_valid_name(name, one_off=False):
match = NAME_RE.match(name)
if match is None:
return False
if one_off:
return match.group(3) == 'run_'
else:
return match.group(3) is None
def parse_name(name):
match = NAME_RE.match(name)
(project, service_name, _, suffix) = match.groups()
return (project, service_name, int(suffix))

View File

@@ -74,8 +74,9 @@ def print_output_event(event, stream, is_terminal):
stream.write("%s %s%s" % (status, event['progress'], terminator))
elif 'progressDetail' in event:
detail = event['progressDetail']
if 'current' in detail:
percentage = float(detail['current']) / float(detail['total']) * 100
total = detail.get('total')
if 'current' in detail and total:
percentage = float(detail['current']) / float(total) * 100
stream.write('%s (%.1f%%)%s' % (status, percentage, terminator))
else:
stream.write('%s%s' % (status, terminator))

View File

@@ -1,13 +1,20 @@
from __future__ import unicode_literals
from __future__ import absolute_import
from functools import reduce
import logging
from functools import reduce
from .config import get_service_name_from_net, ConfigurationError
from .service import Service
from .container import Container
from docker.errors import APIError
from .config import get_service_name_from_net, ConfigurationError
from .const import DEFAULT_TIMEOUT, LABEL_PROJECT, LABEL_SERVICE, LABEL_ONE_OFF
from .container import Container
from .legacy import check_for_legacy_containers
from .service import ContainerNet
from .service import Net
from .service import Service
from .service import ServiceNet
from .utils import parallel_execute
log = logging.getLogger(__name__)
@@ -60,6 +67,12 @@ class Project(object):
self.services = services
self.client = client
def labels(self, one_off=False):
return [
'{0}={1}'.format(LABEL_PROJECT, self.name),
'{0}={1}'.format(LABEL_ONE_OFF, "True" if one_off else "False"),
]
@classmethod
def from_dicts(cls, name, service_dicts, client):
"""
@@ -71,10 +84,20 @@ class Project(object):
volumes_from = project.get_volumes_from(service_dict)
net = project.get_net(service_dict)
project.services.append(Service(client=client, project=name, links=links, net=net,
volumes_from=volumes_from, **service_dict))
project.services.append(
Service(
client=client,
project=name,
links=links,
net=net,
volumes_from=volumes_from,
**service_dict))
return project
@property
def service_names(self):
return [service.name for service in self.services]
def get_service(self, name):
"""
Retrieve a service by name. Raises NoSuchService
@@ -86,6 +109,16 @@ class Project(object):
raise NoSuchService(name)
def validate_service_names(self, service_names):
"""
Validate that the given list of service names only contains valid
services. Raises NoSuchService if one of the names is invalid.
"""
valid_names = self.service_names
for name in service_names:
if name not in valid_names:
raise NoSuchService(name)
def get_services(self, service_names=None, include_deps=False):
"""
Returns a list of this project's services filtered
@@ -102,7 +135,7 @@ class Project(object):
"""
if service_names is None or len(service_names) == 0:
return self.get_services(
service_names=[s.name for s in self.services],
service_names=self.service_names,
include_deps=include_deps
)
else:
@@ -148,38 +181,56 @@ class Project(object):
return volumes_from
def get_net(self, service_dict):
if 'net' in service_dict:
net_name = get_service_name_from_net(service_dict.get('net'))
net = service_dict.pop('net', None)
if not net:
return Net(None)
if net_name:
try:
net = self.get_service(net_name)
except NoSuchService:
try:
net = Container.from_id(self.client, net_name)
except APIError:
raise ConfigurationError('Serivce "%s" is trying to use the network of "%s", which is not the name of a service or container.' % (service_dict['name'], net_name))
else:
net = service_dict['net']
net_name = get_service_name_from_net(net)
if not net_name:
return Net(net)
del service_dict['net']
else:
net = 'bridge'
return net
try:
return ServiceNet(self.get_service(net_name))
except NoSuchService:
pass
try:
return ContainerNet(Container.from_id(self.client, net_name))
except APIError:
raise ConfigurationError(
'Service "%s" is trying to use the network of "%s", '
'which is not the name of a service or container.' % (
service_dict['name'],
net_name))
def start(self, service_names=None, **options):
for service in self.get_services(service_names):
service.start(**options)
def stop(self, service_names=None, **options):
for service in reversed(self.get_services(service_names)):
service.stop(**options)
parallel_execute(
objects=self.containers(service_names),
obj_callable=lambda c: c.stop(**options),
msg_index=lambda c: c.name,
msg="Stopping"
)
def kill(self, service_names=None, **options):
for service in reversed(self.get_services(service_names)):
service.kill(**options)
parallel_execute(
objects=self.containers(service_names),
obj_callable=lambda c: c.kill(**options),
msg_index=lambda c: c.name,
msg="Killing"
)
def remove_stopped(self, service_names=None, **options):
all_containers = self.containers(service_names, stopped=True)
stopped_containers = [c for c in all_containers if not c.is_running]
parallel_execute(
objects=stopped_containers,
obj_callable=lambda c: c.remove(**options),
msg_index=lambda c: c.name,
msg="Removing"
)
def restart(self, service_names=None, **options):
for service in self.get_services(service_names):
@@ -195,46 +246,99 @@ class Project(object):
def up(self,
service_names=None,
start_deps=True,
recreate=True,
insecure_registry=False,
detach=False,
do_build=True):
running_containers = []
for service in self.get_services(service_names, include_deps=start_deps):
if recreate:
for (_, container) in service.recreate_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
running_containers.append(container)
allow_recreate=True,
force_recreate=False,
do_build=True,
timeout=DEFAULT_TIMEOUT):
if force_recreate and not allow_recreate:
raise ValueError("force_recreate and allow_recreate are in conflict")
services = self.get_services(service_names, include_deps=start_deps)
for service in services:
service.remove_duplicate_containers()
plans = self._get_convergence_plans(
services,
allow_recreate=allow_recreate,
force_recreate=force_recreate,
)
return [
container
for service in services
for container in service.execute_convergence_plan(
plans[service.name],
do_build=do_build,
timeout=timeout
)
]
def _get_convergence_plans(self,
services,
allow_recreate=True,
force_recreate=False):
plans = {}
for service in services:
updated_dependencies = [
name
for name in service.get_dependency_names()
if name in plans
and plans[name].action == 'recreate'
]
if updated_dependencies and allow_recreate:
log.debug(
'%s has upstream changes (%s)',
service.name, ", ".join(updated_dependencies),
)
plan = service.convergence_plan(
allow_recreate=allow_recreate,
force_recreate=True,
)
else:
for container in service.start_or_create_containers(
insecure_registry=insecure_registry,
detach=detach,
do_build=do_build):
running_containers.append(container)
plan = service.convergence_plan(
allow_recreate=allow_recreate,
force_recreate=force_recreate,
)
return running_containers
plans[service.name] = plan
def pull(self, service_names=None, insecure_registry=False):
return plans
def pull(self, service_names=None):
for service in self.get_services(service_names, include_deps=True):
service.pull(insecure_registry=insecure_registry)
def remove_stopped(self, service_names=None, **options):
for service in self.get_services(service_names):
service.remove_stopped(**options)
service.pull()
def containers(self, service_names=None, stopped=False, one_off=False):
return [Container.from_ps(self.client, container)
for container in self.client.containers(all=stopped)
for service in self.get_services(service_names)
if service.has_container(container, one_off=one_off)]
if service_names:
self.validate_service_names(service_names)
else:
service_names = self.service_names
containers = filter(None, [
Container.from_ps(self.client, container)
for container in self.client.containers(
all=stopped,
filters={'label': self.labels(one_off=one_off)})])
def matches_service_names(container):
return container.labels.get(LABEL_SERVICE) in service_names
if not containers:
check_for_legacy_containers(
self.client,
self.name,
self.service_names,
)
return filter(matches_service_names, containers)
def _inject_deps(self, acc, service):
net_name = service.get_net_name()
dep_names = (service.get_linked_names() +
service.get_volumes_from_names() +
([net_name] if net_name else []))
dep_names = service.get_dependency_names()
if len(dep_names) > 0:
dep_services = self.get_services(

File diff suppressed because it is too large Load Diff

0
compose/state.py Normal file
View File

100
compose/utils.py Normal file
View File

@@ -0,0 +1,100 @@
import codecs
import hashlib
import json
import logging
import sys
from docker.errors import APIError
from Queue import Queue, Empty
from threading import Thread
log = logging.getLogger(__name__)
def parallel_execute(objects, obj_callable, msg_index, msg):
"""
For a given list of objects, call the callable passing in the first
object we give it.
"""
stream = codecs.getwriter('utf-8')(sys.stdout)
lines = []
errors = {}
for obj in objects:
write_out_msg(stream, lines, msg_index(obj), msg)
q = Queue()
def inner_execute_function(an_callable, parameter, msg_index):
try:
result = an_callable(parameter)
except APIError as e:
errors[msg_index] = e.explanation
result = "error"
except Exception as e:
errors[msg_index] = e
result = 'unexpected_exception'
q.put((msg_index, result))
for an_object in objects:
t = Thread(
target=inner_execute_function,
args=(obj_callable, an_object, msg_index(an_object)),
)
t.daemon = True
t.start()
done = 0
total_to_execute = len(objects)
while done < total_to_execute:
try:
msg_index, result = q.get(timeout=1)
if result == 'unexpected_exception':
raise errors[msg_index]
if result == 'error':
write_out_msg(stream, lines, msg_index, msg, status='error')
else:
write_out_msg(stream, lines, msg_index, msg)
done += 1
except Empty:
pass
if errors:
stream.write("\n")
for error in errors:
stream.write("ERROR: for {} {} \n".format(error, errors[error]))
def write_out_msg(stream, lines, msg_index, msg, status="done"):
"""
Using special ANSI code characters we can write out the msg over the top of
a previous status message, if it exists.
"""
obj_index = msg_index
if msg_index in lines:
position = lines.index(obj_index)
diff = len(lines) - position
# move up
stream.write("%c[%dA" % (27, diff))
# erase
stream.write("%c[2K\r" % 27)
stream.write("{} {}... {}\n".format(msg, obj_index, status))
# move back down
stream.write("%c[%dB" % (27, diff))
else:
diff = 0
lines.append(obj_index)
stream.write("{} {}... \r\n".format(msg, obj_index))
stream.flush()
def json_hash(obj):
dump = json.dumps(obj, sort_keys=True, separators=(',', ':'))
h = hashlib.sha256()
h.update(dump)
return h.hexdigest()

View File

@@ -82,7 +82,7 @@ __docker-compose_services_stopped() {
_docker-compose_build() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--no-cache" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help --no-cache" -- "$cur" ) )
;;
*)
__docker-compose_services_from_build
@@ -94,7 +94,7 @@ _docker-compose_build() {
_docker-compose_docker-compose() {
case "$prev" in
--file|-f)
_filedir y?(a)ml
_filedir "y?(a)ml"
return
;;
--project-name|-p)
@@ -104,7 +104,7 @@ _docker-compose_docker-compose() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help -h --verbose --version --file -f --project-name -p" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help -h --verbose --version -v --file -f --project-name -p" -- "$cur" ) )
;;
*)
COMPREPLY=( $( compgen -W "${commands[*]}" -- "$cur" ) )
@@ -128,7 +128,7 @@ _docker-compose_kill() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-s" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help -s" -- "$cur" ) )
;;
*)
__docker-compose_services_running
@@ -140,7 +140,7 @@ _docker-compose_kill() {
_docker-compose_logs() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--no-color" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help --no-color" -- "$cur" ) )
;;
*)
__docker-compose_services_all
@@ -149,6 +149,15 @@ _docker-compose_logs() {
}
_docker-compose_migrate-to-labels() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
esac
}
_docker-compose_port() {
case "$prev" in
--protocol)
@@ -162,7 +171,7 @@ _docker-compose_port() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--protocol --index" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help --index --protocol" -- "$cur" ) )
;;
*)
__docker-compose_services_all
@@ -174,7 +183,7 @@ _docker-compose_port() {
_docker-compose_ps() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-q" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help -q" -- "$cur" ) )
;;
*)
__docker-compose_services_all
@@ -186,7 +195,7 @@ _docker-compose_ps() {
_docker-compose_pull() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--allow-insecure-ssl" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
__docker-compose_services_from_image
@@ -204,7 +213,7 @@ _docker-compose_restart() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-t --timeout" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help --timeout -t" -- "$cur" ) )
;;
*)
__docker-compose_services_running
@@ -216,7 +225,7 @@ _docker-compose_restart() {
_docker-compose_rm() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--force -f -v" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--force -f --help -v" -- "$cur" ) )
;;
*)
__docker-compose_services_stopped
@@ -239,7 +248,7 @@ _docker-compose_run() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--allow-insecure-ssl -d --entrypoint -e --no-deps --rm --service-ports -T --user -u" -- "$cur" ) )
COMPREPLY=( $( compgen -W "-d --entrypoint -e --help --no-deps --rm --service-ports -T --user -u" -- "$cur" ) )
;;
*)
__docker-compose_services_all
@@ -258,11 +267,24 @@ _docker-compose_scale() {
compopt -o nospace
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
esac
}
_docker-compose_start() {
__docker-compose_services_stopped
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
__docker-compose_services_stopped
;;
esac
}
@@ -275,7 +297,7 @@ _docker-compose_stop() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-t --timeout" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help --timeout -t" -- "$cur" ) )
;;
*)
__docker-compose_services_running
@@ -293,7 +315,7 @@ _docker-compose_up() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--allow-insecure-ssl -d --no-build --no-color --no-deps --no-recreate -t --timeout" -- "$cur" ) )
COMPREPLY=( $( compgen -W "-d --help --no-build --no-color --no-deps --no-recreate --force-recreate --timeout -t" -- "$cur" ) )
;;
*)
__docker-compose_services_all
@@ -302,12 +324,25 @@ _docker-compose_up() {
}
_docker-compose_version() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--short" -- "$cur" ) )
;;
esac
}
_docker-compose() {
local previous_extglob_setting=$(shopt -p extglob)
shopt -s extglob
local commands=(
build
help
kill
logs
migrate-to-labels
port
ps
pull
@@ -318,6 +353,7 @@ _docker-compose() {
start
stop
up
version
)
COMPREPLY=()
@@ -352,6 +388,7 @@ _docker-compose() {
local completions_func=_docker-compose_${command}
declare -F $completions_func >/dev/null && $completions_func
eval "$previous_extglob_setting"
return 0
}

View File

@@ -0,0 +1,321 @@
#compdef docker-compose
# Description
# -----------
# zsh completion for docker-compose
# https://github.com/sdurrheimer/docker-compose-zsh-completion
# -------------------------------------------------------------------------
# Version
# -------
# 0.1.0
# -------------------------------------------------------------------------
# Authors
# -------
# * Steve Durrheimer <s.durrheimer@gmail.com>
# -------------------------------------------------------------------------
# Inspiration
# -----------
# * @albers docker-compose bash completion script
# * @felixr docker zsh completion script : https://github.com/felixr/docker-zsh-completion
# -------------------------------------------------------------------------
# For compatibility reasons, Compose and therefore its completion supports several
# stack compositon files as listed here, in descending priority.
# Support for these filenames might be dropped in some future version.
__docker-compose_compose_file() {
local file
for file in docker-compose.y{,a}ml fig.y{,a}ml ; do
[ -e $file ] && {
echo $file
return
}
done
echo docker-compose.yml
}
# Extracts all service names from docker-compose.yml.
___docker-compose_all_services_in_compose_file() {
local already_selected
local -a services
already_selected=$(echo ${words[@]} | tr " " "|")
awk -F: '/^[a-zA-Z0-9]/{print $1}' "${compose_file:-$(__docker-compose_compose_file)}" 2>/dev/null | grep -Ev "$already_selected"
}
# All services, even those without an existing container
__docker-compose_services_all() {
services=$(___docker-compose_all_services_in_compose_file)
_alternative "args:services:($services)"
}
# All services that have an entry with the given key in their docker-compose.yml section
___docker-compose_services_with_key() {
local already_selected
local -a buildable
already_selected=$(echo ${words[@]} | tr " " "|")
# flatten sections to one line, then filter lines containing the key and return section name.
awk '/^[a-zA-Z0-9]/{printf "\n"};{printf $0;next;}' "${compose_file:-$(__docker-compose_compose_file)}" 2>/dev/null | awk -F: -v key=": +$1:" '$0 ~ key {print $1}' 2>/dev/null | grep -Ev "$already_selected"
}
# All services that are defined by a Dockerfile reference
__docker-compose_services_from_build() {
buildable=$(___docker-compose_services_with_key build)
_alternative "args:buildable services:($buildable)"
}
# All services that are defined by an image
__docker-compose_services_from_image() {
pullable=$(___docker-compose_services_with_key image)
_alternative "args:pullable services:($pullable)"
}
__docker-compose_get_services() {
local kind expl
declare -a running stopped lines args services
docker_status=$(docker ps > /dev/null 2>&1)
if [ $? -ne 0 ]; then
_message "Error! Docker is not running."
return 1
fi
kind=$1
shift
[[ $kind = (stopped|all) ]] && args=($args -a)
lines=(${(f)"$(_call_program commands docker ps ${args})"})
services=(${(f)"$(_call_program commands docker-compose 2>/dev/null ${compose_file:+-f $compose_file} ${compose_project:+-p $compose_project} ps -q)"})
# Parse header line to find columns
local i=1 j=1 k header=${lines[1]}
declare -A begin end
while (( $j < ${#header} - 1 )) {
i=$(( $j + ${${header[$j,-1]}[(i)[^ ]]} - 1))
j=$(( $i + ${${header[$i,-1]}[(i) ]} - 1))
k=$(( $j + ${${header[$j,-1]}[(i)[^ ]]} - 2))
begin[${header[$i,$(($j-1))]}]=$i
end[${header[$i,$(($j-1))]}]=$k
}
lines=(${lines[2,-1]})
# Container ID
local line s name
local -a names
for line in $lines; do
if [[ $services == *"${line[${begin[CONTAINER ID]},${end[CONTAINER ID]}]%% ##}"* ]]; then
names=(${(ps:,:)${${line[${begin[NAMES]},-1]}%% *}})
for name in $names; do
s="${${name%_*}#*_}:${(l:15:: :::)${${line[${begin[CREATED]},${end[CREATED]}]/ ago/}%% ##}}"
s="$s, ${line[${begin[CONTAINER ID]},${end[CONTAINER ID]}]%% ##}"
s="$s, ${${${line[$begin[IMAGE],$end[IMAGE]]}/:/\\:}%% ##}"
if [[ ${line[${begin[STATUS]},${end[STATUS]}]} = Exit* ]]; then
stopped=($stopped $s)
else
running=($running $s)
fi
done
fi
done
[[ $kind = (running|all) ]] && _describe -t services-running "running services" running
[[ $kind = (stopped|all) ]] && _describe -t services-stopped "stopped services" stopped
}
__docker-compose_stoppedservices() {
__docker-compose_get_services stopped "$@"
}
__docker-compose_runningservices() {
__docker-compose_get_services running "$@"
}
__docker-compose_services () {
__docker-compose_get_services all "$@"
}
__docker-compose_caching_policy() {
oldp=( "$1"(Nmh+1) ) # 1 hour
(( $#oldp ))
}
__docker-compose_commands () {
local cache_policy
zstyle -s ":completion:${curcontext}:" cache-policy cache_policy
if [[ -z "$cache_policy" ]]; then
zstyle ":completion:${curcontext}:" cache-policy __docker-compose_caching_policy
fi
if ( [[ ${+_docker_compose_subcommands} -eq 0 ]] || _cache_invalid docker_compose_subcommands) \
&& ! _retrieve_cache docker_compose_subcommands;
then
local -a lines
lines=(${(f)"$(_call_program commands docker-compose 2>&1)"})
_docker_compose_subcommands=(${${${lines[$((${lines[(i)Commands:]} + 1)),${lines[(I) *]}]}## #}/ ##/:})
_store_cache docker_compose_subcommands _docker_compose_subcommands
fi
_describe -t docker-compose-commands "docker-compose command" _docker_compose_subcommands
}
__docker-compose_subcommand () {
local -a _command_args
integer ret=1
case "$words[1]" in
(build)
_arguments \
'--help[Print usage]' \
'--no-cache[Do not use cache when building the image]' \
'*:services:__docker-compose_services_from_build' && ret=0
;;
(help)
_arguments ':subcommand:__docker-compose_commands' && ret=0
;;
(kill)
_arguments \
'--help[Print usage]' \
'-s[SIGNAL to send to the container. Default signal is SIGKILL.]:signal:_signals' \
'*:running services:__docker-compose_runningservices' && ret=0
;;
(logs)
_arguments \
'--help[Print usage]' \
'--no-color[Produce monochrome output.]' \
'*:services:__docker-compose_services_all' && ret=0
;;
(migrate-to-labels)
_arguments -A '-*' \
'--help[Print usage]' \
'(-):Recreate containers to add labels' && ret=0
;;
(port)
_arguments \
'--help[Print usage]' \
'--protocol=-[tcp or udap (defaults to tcp)]:protocol:(tcp udp)' \
'--index=-[index of the container if there are mutiple instances of a service (defaults to 1)]:index: ' \
'1:running services:__docker-compose_runningservices' \
'2:port:_ports' && ret=0
;;
(ps)
_arguments \
'--help[Print usage]' \
'-q[Only display IDs]' \
'*:services:__docker-compose_services_all' && ret=0
;;
(pull)
_arguments \
'--help[Print usage]' \
'*:services:__docker-compose_services_from_image' && ret=0
;;
(rm)
_arguments \
'(-f --force)'{-f,--force}"[Don't ask to confirm removal]" \
'--help[Print usage]' \
'-v[Remove volumes associated with containers]' \
'*:stopped services:__docker-compose_stoppedservices' && ret=0
;;
(run)
_arguments \
'-d[Detached mode: Run container in the background, print new container name.]' \
'--entrypoint[Overwrite the entrypoint of the image.]:entry point: ' \
'*-e[KEY=VAL Set an environment variable (can be used multiple times)]:environment variable KEY=VAL: ' \
'--help[Print usage]' \
'(-u --user)'{-u,--user=-}'[Run as specified username or uid]:username or uid:_users' \
"--no-deps[Don't start linked services.]" \
'--rm[Remove container after run. Ignored in detached mode.]' \
"--service-ports[Run command with the service's ports enabled and mapped to the host.]" \
'-T[Disable pseudo-tty allocation. By default `docker-compose run` allocates a TTY.]' \
'(-):services:__docker-compose_services' \
'(-):command: _command_names -e' \
'*::arguments: _normal' && ret=0
;;
(scale)
_arguments \
'--help[Print usage]' \
'*:running services:__docker-compose_runningservices' && ret=0
;;
(start)
_arguments \
'--help[Print usage]' \
'*:stopped services:__docker-compose_stoppedservices' && ret=0
;;
(stop|restart)
_arguments \
'--help[Print usage]' \
'(-t --timeout)'{-t,--timeout}"[Specify a shutdown timeout in seconds. (default: 10)]:seconds: " \
'*:running services:__docker-compose_runningservices' && ret=0
;;
(up)
_arguments \
'-d[Detached mode: Run containers in the background, print new container names.]' \
'--help[Print usage]' \
'--no-color[Produce monochrome output.]' \
"--no-deps[Don't start linked services.]" \
"--no-recreate[If containers already exist, don't recreate them.]" \
"--force-recreate[Recreate containers even if their configuration and image haven't changed]" \
"--no-build[Don't build an image, even if it's missing]" \
'(-t --timeout)'{-t,--timeout}"[Specify a shutdown timeout in seconds. (default: 10)]:seconds: " \
'*:services:__docker-compose_services_all' && ret=0
;;
(version)
_arguments \
'--help[Print usage]' \
"--short[Shows only Compose's version number.]" && ret=0
;;
(*)
_message 'Unknown sub command'
esac
return ret
}
_docker-compose () {
# Support for subservices, which allows for `compdef _docker docker-shell=_docker_containers`.
# Based on /usr/share/zsh/functions/Completion/Unix/_git without support for `ret`.
if [[ $service != docker-compose ]]; then
_call_function - _$service
return
fi
local curcontext="$curcontext" state line ret=1
typeset -A opt_args
_arguments -C \
'(- :)'{-h,--help}'[Get help]' \
'--verbose[Show more output]' \
'(- :)'{-v,--version}'[Print version and exit]' \
'(-f --file)'{-f,--file}'[Specify an alternate docker-compose file (default: docker-compose.yml)]:file:_files -g "*.yml"' \
'(-p --project-name)'{-p,--project-name}'[Specify an alternate project name (default: directory name)]:project name:' \
'(-): :->command' \
'(-)*:: :->option-or-argument' && ret=0
local counter=1
#local compose_file compose_project
while [ $counter -lt ${#words[@]} ]; do
case "${words[$counter]}" in
-f|--file)
(( counter++ ))
compose_file="${words[$counter]}"
;;
-p|--project-name)
(( counter++ ))
compose_project="${words[$counter]}"
;;
*)
;;
esac
(( counter++ ))
done
case $state in
(command)
__docker-compose_commands && ret=0
;;
(option-or-argument)
curcontext=${curcontext%:*:*}:docker-compose-$words[1]:
__docker-compose_subcommand && ret=0
;;
esac
return ret
}
_docker-compose "$@"

BIN
docker Executable file

Binary file not shown.

View File

@@ -1,15 +1,26 @@
FROM docs/base:latest
MAINTAINER Sven Dowideit <SvenDowideit@docker.com> (@SvenDowideit)
MAINTAINER Mary Anthony <mary@docker.com> (@moxiegirl)
# to get the git info for this repo
# To get the git info for this repo
COPY . /src
# Reset the /docs dir so we can replace the theme meta with the new repo's git info
RUN git reset --hard
COPY . /docs/content/compose/
RUN grep "__version" /src/compose/__init__.py | sed "s/.*'\(.*\)'/\1/" > /docs/VERSION
COPY docs/* /docs/sources/compose/
COPY docs/mkdocs.yml /docs/mkdocs-compose.yml
RUN svn checkout https://github.com/docker/docker/trunk/docs /docs/content/docker
RUN svn checkout https://github.com/docker/swarm/trunk/docs /docs/content/swarm
RUN svn checkout https://github.com/docker/machine/trunk/docs /docs/content/machine
RUN svn checkout https://github.com/docker/distribution/trunk/docs /docs/content/registry
RUN svn checkout https://github.com/docker/tutorials/trunk/docs /docs/content/tutorials
RUN svn checkout https://github.com/docker/opensource/trunk/docs /docs/content
# Then build everything together, ready for mkdocs
RUN /docs/build.sh
# Sed to process GitHub Markdown
# 1-2 Remove comment code from metadata block
# 3 Change ](/word to ](/project/ in links
# 4 Change ](word.md) to ](/project/word)
# 5 Remove .md extension from link text
# 6 Change ](../ to ](/project/word)
# 7 Change ](../../ to ](/project/ --> not implemented
#
#
RUN /src/pre-process.sh /docs

55
docs/Makefile Normal file
View File

@@ -0,0 +1,55 @@
.PHONY: all binary build cross default docs docs-build docs-shell shell test test-unit test-integration test-integration-cli test-docker-py validate
# env vars passed through directly to Docker's build scripts
# to allow things like `make DOCKER_CLIENTONLY=1 binary` easily
# `docs/sources/contributing/devenvironment.md ` and `project/PACKAGERS.md` have some limited documentation of some of these
DOCKER_ENVS := \
-e BUILDFLAGS \
-e DOCKER_CLIENTONLY \
-e DOCKER_EXECDRIVER \
-e DOCKER_GRAPHDRIVER \
-e TESTDIRS \
-e TESTFLAGS \
-e TIMEOUT
# note: we _cannot_ add "-e DOCKER_BUILDTAGS" here because even if it's unset in the shell, that would shadow the "ENV DOCKER_BUILDTAGS" set in our Dockerfile, which is very important for our official builds
# to allow `make DOCSDIR=docs docs-shell` (to create a bind mount in docs)
DOCS_MOUNT := $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR))
# to allow `make DOCSPORT=9000 docs`
DOCSPORT := 8000
# Get the IP ADDRESS
DOCKER_IP=$(shell python -c "import urlparse ; print urlparse.urlparse('$(DOCKER_HOST)').hostname or ''")
HUGO_BASE_URL=$(shell test -z "$(DOCKER_IP)" && echo localhost || echo "$(DOCKER_IP)")
HUGO_BIND_IP=0.0.0.0
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
DOCKER_IMAGE := docker$(if $(GIT_BRANCH),:$(GIT_BRANCH))
DOCKER_DOCS_IMAGE := docs-base$(if $(GIT_BRANCH),:$(GIT_BRANCH))
DOCKER_RUN_DOCS := docker run --rm -it $(DOCS_MOUNT) -e AWS_S3_BUCKET -e NOCACHE
# for some docs workarounds (see below in "docs-build" target)
GITCOMMIT := $(shell git rev-parse --short HEAD 2>/dev/null)
default: docs
docs: docs-build
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 -e DOCKERHOST "$(DOCKER_DOCS_IMAGE)" hugo server --port=$(DOCSPORT) --baseUrl=$(HUGO_BASE_URL) --bind=$(HUGO_BIND_IP)
docs-draft: docs-build
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 -e DOCKERHOST "$(DOCKER_DOCS_IMAGE)" hugo server --buildDrafts="true" --port=$(DOCSPORT) --baseUrl=$(HUGO_BASE_URL) --bind=$(HUGO_BIND_IP)
docs-shell: docs-build
$(DOCKER_RUN_DOCS) -p $(if $(DOCSPORT),$(DOCSPORT):)8000 "$(DOCKER_DOCS_IMAGE)" bash
docs-build:
# ( git remote | grep -v upstream ) || git diff --name-status upstream/release..upstream/docs ./ > ./changed-files
# echo "$(GIT_BRANCH)" > GIT_BRANCH
# echo "$(AWS_S3_BUCKET)" > AWS_S3_BUCKET
# echo "$(GITCOMMIT)" > GITCOMMIT
docker build -t "$(DOCKER_DOCS_IMAGE)" .

77
docs/README.md Normal file
View File

@@ -0,0 +1,77 @@
# Contributing to the Docker Compose documentation
The documentation in this directory is part of the [https://docs.docker.com](https://docs.docker.com) website. Docker uses [the Hugo static generator](http://gohugo.io/overview/introduction/) to convert project Markdown files to a static HTML site.
You don't need to be a Hugo expert to contribute to the compose documentation. If you are familiar with Markdown, you can modify the content in the `docs` files.
If you want to add a new file or change the location of the document in the menu, you do need to know a little more.
## Documentation contributing workflow
1. Edit a Markdown file in the tree.
2. Save your changes.
3. Make sure you are in the `docs` subdirectory.
4. Build the documentation.
$ make docs
---> ffcf3f6c4e97
Removing intermediate container a676414185e8
Successfully built ffcf3f6c4e97
docker run --rm -it -e AWS_S3_BUCKET -e NOCACHE -p 8000:8000 -e DOCKERHOST "docs-base:test-tooling" hugo server --port=8000 --baseUrl=192.168.59.103 --bind=0.0.0.0
ERROR: 2015/06/13 MenuEntry's .Url is deprecated and will be removed in Hugo 0.15. Use .URL instead.
0 of 4 drafts rendered
0 future content
12 pages created
0 paginator pages created
0 tags created
0 categories created
in 55 ms
Serving pages from /docs/public
Web Server is available at http://0.0.0.0:8000/
Press Ctrl+C to stop
5. Open the available server in your browser.
The documentation server has the complete menu but only the Docker Compose
documentation resolves. You can't access the other project docs from this
localized build.
## Tips on Hugo metadata and menu positioning
The top of each Docker Compose documentation file contains TOML metadata. The metadata is commented out to prevent it from appearing in GitHub.
<!--[metadata]>
+++
title = "Extending services in Compose"
description = "How to use Docker Compose's extends keyword to share configuration between files and projects"
keywords = ["fig, composition, compose, docker, orchestration, documentation, docs"]
[menu.main]
parent="smn_workw_compose"
weight=2
+++
<![end-metadata]-->
The metadata alone has this structure:
+++
title = "Extending services in Compose"
description = "How to use Docker Compose's extends keyword to share configuration between files and projects"
keywords = ["fig, composition, compose, docker, orchestration, documentation, docs"]
[menu.main]
parent="smn_workw_compose"
weight=2
+++
The `[menu.main]` section refers to navigation defined [in the main Docker menu](https://github.com/docker/docs-base/blob/hugo/config.toml). This metadata says *add a menu item called* Extending services in Compose *to the menu with the* `smn_workdw_compose` *identifier*. If you locate the menu in the configuration, you'll find *Create multi-container applications* is the menu title.
You can move an article in the tree by specifying a new parent. You can shift the location of the item by changing its weight. Higher numbers are heavier and shift the item to the bottom of menu. Low or no numbers shift it up.
## Other key documentation repositories
The `docker/docs-base` repository contains [the Hugo theme and menu configuration](https://github.com/docker/docs-base). If you open the `Dockerfile` you'll see the `make docs` relies on this as a base image for building the Compose documentation.
The `docker/docs.docker.com` repository contains [build system for building the Docker documentation site](https://github.com/docker/docs.docker.com). Fork this repository to build the entire documentation site.

View File

@@ -1,181 +0,0 @@
page_title: Compose CLI reference
page_description: Compose CLI reference
page_keywords: fig, composition, compose, docker, orchestration, cli, reference
# CLI reference
Most Docker Compose commands are run against one or more services. If
the service is not specified, the command will apply to all services.
For full usage information, run `docker-compose [COMMAND] --help`.
## Commands
### build
Builds or rebuilds services.
Services are built once and then tagged as `project_service`, e.g.,
`composetest_db`. If you change a service's Dockerfile or the contents of its
build directory, run `docker-compose build` to rebuild it.
### help
Displays help and usage instructions for a command.
### kill
Forces running containers to stop by sending a `SIGKILL` signal. Optionally the
signal can be passed, for example:
$ docker-compose kill -s SIGINT
### logs
Displays log output from services.
### port
Prints the public port for a port binding
### ps
Lists containers.
### pull
Pulls service images.
### rm
Removes stopped service containers.
### run
Runs a one-off command on a service.
For example,
$ docker-compose run web python manage.py shell
will start the `web` service and then run `manage.py shell` in python.
Note that by default, linked services will also be started, unless they are
already running.
One-off commands are started in new containers with the same configuration as a
normal container for that service, so volumes, links, etc will all be created as
expected. When using `run`, there are two differences from bringing up a
container normally:
1. the command will be overridden with the one specified. So, if you run
`docker-compose run web bash`, the container's web command (which could default
to, e.g., `python app.py`) will be overridden to `bash`
2. by default no ports will be created in case they collide with already opened
ports.
Links are also created between one-off commands and the other containers which
are part of that service. So, for example, you could run:
$ docker-compose run db psql -h db -U docker
This would open up an interactive PostgreSQL shell for the linked `db` container
(which would get created or started as needed).
If you do not want linked containers to start when running the one-off command,
specify the `--no-deps` flag:
$ docker-compose run --no-deps web python manage.py shell
Similarly, if you do want the service's ports to be created and mapped to the
host, specify the `--service-ports` flag:
$ docker-compose run --service-ports web python manage.py shell
### scale
Sets the number of containers to run for a service.
Numbers are specified as arguments in the form `service=num`. For example:
$ docker-compose scale web=2 worker=3
### start
Starts existing containers for a service.
### stop
Stops running containers without removing them. They can be started again with
`docker-compose start`.
### up
Builds, (re)creates, starts, and attaches to containers for a service.
Linked services will be started, unless they are already running.
By default, `docker-compose up` will aggregate the output of each container and,
when it exits, all containers will be stopped. Running `docker-compose up -d`,
will start the containers in the background and leave them running.
By default, if there are existing containers for a service, `docker-compose up` will stop and recreate them (preserving mounted volumes with [volumes-from]), so that changes in `docker-compose.yml` are picked up. If you do not want containers stopped and recreated, use `docker-compose up --no-recreate`. This will still start any stopped containers, if needed.
[volumes-from]: http://docs.docker.io/en/latest/use/working_with_volumes/
## Options
### --verbose
Shows more output
### --version
Prints version and exits
### -f, --file FILE
Specifies an alternate Compose yaml file (default: `docker-compose.yml`)
### -p, --project-name NAME
Specifies an alternate project name (default: current directory name)
## Environment Variables
Several environment variables are available for you to configure Compose's behaviour.
Variables starting with `DOCKER_` are the same as those used to configure the
Docker command-line client. If you're using boot2docker, `$(boot2docker shellinit)`
will set them to their correct values.
### COMPOSE\_PROJECT\_NAME
Sets the project name, which is prepended to the name of every container started by Compose. Defaults to the `basename` of the current working directory.
### COMPOSE\_FILE
Sets the path to the `docker-compose.yml` to use. Defaults to `docker-compose.yml` in the current working directory.
### DOCKER\_HOST
Sets the URL of the docker daemon. As with the Docker client, defaults to `unix:///var/run/docker.sock`.
### DOCKER\_TLS\_VERIFY
When set to anything other than an empty string, enables TLS communication with
the daemon.
### DOCKER\_CERT\_PATH
Configures the path to the `ca.pem`, `cert.pem`, and `key.pem` files used for TLS verification. Defaults to `~/.docker`.
## Compose documentation
- [Installing Compose](install.md)
- [User guide](index.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)
- [Compose command line completion](completion.md)

View File

@@ -1,28 +1,53 @@
---
layout: default
title: Command Completion
---
<!--[metadata]>
+++
title = "Command Completion"
description = "Compose CLI reference"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
[menu.main]
parent="smn_workw_compose"
weight=3
+++
<![end-metadata]-->
Command Completion
==================
# Command Completion
Compose comes with [command completion](http://en.wikipedia.org/wiki/Command-line_completion)
for the bash shell.
for the bash and zsh shell.
Installing Command Completion
-----------------------------
## Installing Command Completion
### Bash
Make sure bash completion is installed. If you use a current Linux in a non-minimal installation, bash completion should be available.
On a Mac, install with `brew install bash-completion`
Place the completion script in `/etc/bash_completion.d/` (`/usr/local/etc/bash_completion.d/` on a Mac), using e.g.
curl -L https://raw.githubusercontent.com/docker/compose/1.2.0/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose
Place the completion script in `/etc/bash_completion.d/` (`/usr/local/etc/bash_completion.d/` on a Mac), using e.g.
curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose --version | awk 'NR==1{print $NF}')/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose
Completion will be available upon next login.
Available completions
---------------------
### Zsh
Place the completion script in your `/path/to/zsh/completion`, using e.g. `~/.zsh/completion/`
mkdir -p ~/.zsh/completion
curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose --version | awk 'NR==1{print $NF}')/contrib/completion/zsh/_docker-compose > ~/.zsh/completion/_docker-compose
Include the directory in your `$fpath`, e.g. by adding in `~/.zshrc`
fpath=(~/.zsh/completion $fpath)
Make sure `compinit` is loaded or do it by adding in `~/.zshrc`
autoload -Uz compinit && compinit -i
Then reload your shell
exec $SHELL -l
## Available completions
Depending on what you typed on the command line so far, it will complete
- available docker-compose commands
@@ -34,8 +59,11 @@ Enjoy working with Compose faster and with less typos!
## Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [User guide](index.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)

View File

@@ -1,10 +1,16 @@
page_title: Quickstart Guide: Compose and Django
page_description: Getting started with Docker Compose and Django
page_keywords: documentation, docs, docker, compose, orchestration, containers,
django
<!--[metadata]>
+++
title = "Quickstart Guide: Compose and Django"
description = "Getting started with Docker Compose and Django"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
weight=4
+++
<![end-metadata]-->
## Getting started with Compose and Django
## Quickstart Guide: Compose and Django
This Quick-start Guide will demonstrate how to use Compose to set up and run a
@@ -55,7 +61,7 @@ mounted inside the containers, and what ports they expose.
links:
- db
See the [`docker-compose.yml` reference](yml.html) for more information on how
See the [`docker-compose.yml` reference](yml.md) for more information on how
this file works.
### Build the project
@@ -109,8 +115,7 @@ Then, run `docker-compose up`:
myapp_web_1 | Starting development server at http://0.0.0.0:8000/
myapp_web_1 | Quit the server with CONTROL-C.
Your Django app should nw be running at port 8000 on your Docker daemon (if
you're using Boot2docker, `boot2docker ip` will tell you its address).
Your Django app should nw be running at port 8000 on your Docker daemon. If you are using a Docker Machine VM, you can use the `docker-machine ip MACHINE_NAME` to get the IP address.
You can also run management commands with Docker. To set up your database, for
example, run `docker-compose up` and in another terminal run:
@@ -119,8 +124,11 @@ example, run `docker-compose up` and in another terminal run:
## More Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [User guide](index.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)

View File

@@ -1,10 +1,15 @@
---
layout: default
title: Compose environment variables reference
---
<!--[metadata]>
+++
title = "Compose environment variables reference"
description = "Compose CLI reference"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
[menu.main]
parent="smn_compose_ref"
weight=3
+++
<![end-metadata]-->
Environment variables reference
===============================
# Compose environment variables reference
**Note:** Environment variables are no longer the recommended method for connecting to linked services. Instead, you should use the link name (by default, the name of the linked service) as the hostname to connect to. See the [docker-compose.yml documentation](yml.md#links) for details.
@@ -34,8 +39,11 @@ Fully qualified container name, e.g. `DB_1_NAME=/myapp_web_1/myapp_db_1`
## Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [User guide](index.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose command line completion](completion.md)

363
docs/extends.md Normal file
View File

@@ -0,0 +1,363 @@
<!--[metadata]>
+++
title = "Extending services in Compose"
description = "How to use Docker Compose's extends keyword to share configuration between files and projects"
keywords = ["fig, composition, compose, docker, orchestration, documentation, docs"]
[menu.main]
parent="smn_workw_compose"
weight=2
+++
<![end-metadata]-->
## Extending services in Compose
Docker Compose's `extends` keyword enables sharing of common configurations
among different files, or even different projects entirely. Extending services
is useful if you have several applications that reuse commonly-defined services.
Using `extends` you can define a service in one place and refer to it from
anywhere.
Alternatively, you can deploy the same application to multiple environments with
a slightly different set of services in each case (or with changes to the
configuration of some services). Moreover, you can do so without copy-pasting
the configuration around.
### Understand the extends configuration
When defining any service in `docker-compose.yml`, you can declare that you are
extending another service like this:
web:
extends:
file: common-services.yml
service: webapp
This instructs Compose to re-use the configuration for the `webapp` service
defined in the `common-services.yml` file. Suppose that `common-services.yml`
looks like this:
webapp:
build: .
ports:
- "8000:8000"
volumes:
- "/data"
In this case, you'll get exactly the same result as if you wrote
`docker-compose.yml` with that `build`, `ports` and `volumes` configuration
defined directly under `web`.
You can go further and define (or re-define) configuration locally in
`docker-compose.yml`:
web:
extends:
file: common-services.yml
service: webapp
environment:
- DEBUG=1
cpu_shares: 5
You can also write other services and link your `web` service to them:
web:
extends:
file: common-services.yml
service: webapp
environment:
- DEBUG=1
cpu_shares: 5
links:
- db
db:
image: postgres
For full details on how to use `extends`, refer to the [reference](#reference).
### Example use case
In this example, youll repurpose the example app from the [quick start
guide](index.md). (If you're not familiar with Compose, it's recommended that
you go through the quick start first.) This example assumes you want to use
Compose both to develop an application locally and then deploy it to a
production environment.
The local and production environments are similar, but there are some
differences. In development, you mount the application code as a volume so that
it can pick up changes; in production, the code should be immutable from the
outside. This ensures its not accidentally changed. The development environment
uses a local Redis container, but in production another team manages the Redis
service, which is listening at `redis-production.example.com`.
To configure with `extends` for this sample, you must:
1. Define the web application as a Docker image in `Dockerfile` and a Compose
service in `common.yml`.
2. Define the development environment in the standard Compose file,
`docker-compose.yml`.
- Use `extends` to pull in the web service.
- Configure a volume to enable code reloading.
- Create an additional Redis service for the application to use locally.
3. Define the production environment in a third Compose file, `production.yml`.
- Use `extends` to pull in the web service.
- Configure the web service to talk to the external, production Redis service.
#### Define the web app
Defining the web application requires the following:
1. Create an `app.py` file.
This file contains a simple Python application that uses Flask to serve HTTP
and increments a counter in Redis:
from flask import Flask
from redis import Redis
import os
app = Flask(__name__)
redis = Redis(host=os.environ['REDIS_HOST'], port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return 'Hello World! I have been seen %s times.\n' % redis.get('hits')
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
This code uses a `REDIS_HOST` environment variable to determine where to
find Redis.
2. Define the Python dependencies in a `requirements.txt` file:
flask
redis
3. Create a `Dockerfile` to build an image containing the app:
FROM python:2.7
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD python app.py
4. Create a Compose configuration file called `common.yml`:
This configuration defines how to run the app.
web:
build: .
ports:
- "5000:5000"
Typically, you would have dropped this configuration into
`docker-compose.yml` file, but in order to pull it into multiple files with
`extends`, it needs to be in a separate file.
#### Define the development environment
1. Create a `docker-compose.yml` file.
The `extends` option pulls in the `web` service from the `common.yml` file
you created in the previous section.
web:
extends:
file: common.yml
service: web
volumes:
- .:/code
links:
- redis
environment:
- REDIS_HOST=redis
redis:
image: redis
The new addition defines a `web` service that:
- Fetches the base configuration for `web` out of `common.yml`.
- Adds `volumes` and `links` configuration to the base (`common.yml`)
configuration.
- Sets the `REDIS_HOST` environment variable to point to the linked redis
container. This environment uses a stock `redis` image from the Docker Hub.
2. Run `docker-compose up`.
Compose creates, links, and starts a web and redis container linked together.
It mounts your application code inside the web container.
3. Verify that the code is mounted by changing the message in
`app.py`&mdash;say, from `Hello world!` to `Hello from Compose!`.
Don't forget to refresh your browser to see the change!
#### Define the production environment
You are almost done. Now, define your production environment:
1. Create a `production.yml` file.
As with `docker-compose.yml`, the `extends` option pulls in the `web` service
from `common.yml`.
web:
extends:
file: common.yml
service: web
environment:
- REDIS_HOST=redis-production.example.com
2. Run `docker-compose -f production.yml up`.
Compose creates *just* a web container and configures the Redis connection via
the `REDIS_HOST` environment variable. This variable points to the production
Redis instance.
> **Note**: If you try to load up the webapp in your browser you'll get an
> error&mdash;`redis-production.example.com` isn't actually a Redis server.
You've now done a basic `extends` configuration. As your application develops,
you can make any necessary changes to the web service in `common.yml`. Compose
picks up both the development and production environments when you next run
`docker-compose`. You don't have to do any copy-and-paste, and you don't have to
manually keep both environments in sync.
### Reference
You can use `extends` on any service together with other configuration keys. It
always expects a dictionary that should always contain the key: `service` and optionally the `file` key.
The `file` key specifies the location of a Compose configuration file defining
the extension. The `file` value can be an absolute or relative path. If you
specify a relative path, Docker Compose treats it as relative to the location
of the current file. If you don't specify a `file`, Compose looks in the
current configuration file.
The `service` key specifies the name of the service to extend, for example `web`
or `database`.
You can extend a service that itself extends another. You can extend
indefinitely. Compose does not support circular references and `docker-compose`
returns an error if it encounters them.
#### Adding and overriding configuration
Compose copies configurations from the original service over to the local one,
**except** for `links` and `volumes_from`. These exceptions exist to avoid
implicit dependencies&mdash;you always define `links` and `volumes_from`
locally. This ensures dependencies between services are clearly visible when
reading the current file. Defining these locally also ensures changes to the
referenced file don't result in breakage.
If a configuration option is defined in both the original service and the local
service, the local value either *override*s or *extend*s the definition of the
original service. This works differently for other configuration options.
For single-value options like `image`, `command` or `mem_limit`, the new value
replaces the old value. **This is the default behaviour - all exceptions are
listed below.**
# original service
command: python app.py
# local service
command: python otherapp.py
# result
command: python otherapp.py
In the case of `build` and `image`, using one in the local service causes
Compose to discard the other, if it was defined in the original service.
# original service
build: .
# local service
image: redis
# result
image: redis
# original service
image: redis
# local service
build: .
# result
build: .
For the **multi-value options** `ports`, `expose`, `external_links`, `dns` and
`dns_search`, Compose concatenates both sets of values:
# original service
expose:
- "3000"
# local service
expose:
- "4000"
- "5000"
# result
expose:
- "3000"
- "4000"
- "5000"
In the case of `environment` and `labels`, Compose "merges" entries together
with locally-defined values taking precedence:
# original service
environment:
- FOO=original
- BAR=original
# local service
environment:
- BAR=local
- BAZ=local
# result
environment:
- FOO=original
- BAR=local
- BAZ=local
Finally, for `volumes` and `devices`, Compose "merges" entries together with
locally-defined bindings taking precedence:
# original service
volumes:
- /original-dir/foo:/foo
- /original-dir/bar:/bar
# local service
volumes:
- /local-dir/bar:/bar
- /local-dir/baz/:baz
# result
volumes:
- /original-dir/foo:/foo
- /local-dir/bar:/bar
- /local-dir/baz/:baz
## Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose command line completion](completion.md)

View File

@@ -1,47 +1,44 @@
page_title: Compose: Multi-container orchestration for Docker
page_description: Introduction and Overview of Compose
page_keywords: documentation, docs, docker, compose, orchestration, containers
<!--[metadata]>
+++
title = "Overview of Docker Compose"
description = "Introduction and Overview of Compose"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
+++
<![end-metadata]-->
# Docker Compose
# Overview of Docker Compose
Compose is a tool for defining and running complex applications with Docker.
With Compose, you define a multi-container application in a single file, then
spin your application up in a single command which does everything that needs to
be done to get it running.
Compose is a tool for defining and running multi-container applications with
Docker. With Compose, you define a multi-container application in a single
file, then spin your application up in a single command which does everything
that needs to be done to get it running.
Compose is great for development environments, staging servers, and CI. We don't
recommend that you use it in production yet.
Using Compose is basically a three-step process.
First, you define your app's environment with a `Dockerfile` so it can be
reproduced anywhere:
```Dockerfile
FROM python:2.7
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code
CMD python app.py
```
Next, you define the services that make up your app in `docker-compose.yml` so
1. Define your app's environment with a `Dockerfile` so it can be
reproduced anywhere.
2. Define the services that make up your app in `docker-compose.yml` so
they can be run together in an isolated environment:
3. Lastly, run `docker-compose up` and Compose will start and run your entire app.
```yaml
web:
build: .
links:
- db
ports:
- "8000:8000"
db:
image: postgres
```
A `docker-compose.yml` looks like this:
Lastly, run `docker-compose up` and Compose will start and run your entire app.
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
Compose has commands for managing the whole lifecycle of your application:
@@ -53,6 +50,9 @@ Compose has commands for managing the whole lifecycle of your application:
## Compose documentation
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)
@@ -75,23 +75,21 @@ Next, you'll want to make a directory for the project:
$ cd composetest
Inside this directory, create `app.py`, a simple web app that uses the Flask
framework and increments a value in Redis:
framework and increments a value in Redis. Don't worry if you don't have Redis installed, docker is going to take care of that for you when we [define services](#define-services):
```python
from flask import Flask
from redis import Redis
import os
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
from flask import Flask
from redis import Redis
@app.route('/')
def hello():
redis.incr('hits')
return 'Hello World! I have been seen %s times.' % redis.get('hits')
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
```
@app.route('/')
def hello():
redis.incr('hits')
return 'Hello World! I have been seen %s times.' % redis.get('hits')
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
Next, define the Python dependencies in a file called `requirements.txt`:
@@ -108,13 +106,19 @@ specify how to build the image using a file called
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD python app.py
This tells Docker to include Python, your code, and your Python dependencies in
a Docker image. For more information on how to write Dockerfiles, see the
[Docker user
guide](https://docs.docker.com/userguide/dockerimages/#building-an-image-from-a-dockerfile)
and the
[Dockerfile reference](http://docs.docker.com/reference/builder/).
This tells Docker to:
* Build an image starting with the Python 2.7 image.
* Add the current directory `.` into the path `/code` in the image.
* Set the working directory to `/code`.
* Install your Python dependencies.
* Set the default command for the container to `python app.py`
For more information on how to write Dockerfiles, see the [Docker user guide](https://docs.docker.com/userguide/dockerimages/#building-an-image-from-a-dockerfile) and the [Dockerfile reference](http://docs.docker.com/reference/builder/).
You can test that this builds by running `docker build -t web .`.
### Define services
@@ -122,7 +126,6 @@ Next, define a set of services using `docker-compose.yml`:
web:
build: .
command: python app.py
ports:
- "5000:5000"
volumes:
@@ -134,19 +137,20 @@ Next, define a set of services using `docker-compose.yml`:
This defines two services:
- `web`, which is built from the `Dockerfile` in the current directory. It also
says to run the command `python app.py` inside the image, forward the exposed
port 5000 on the container to port 5000 on the host machine, connect up the
Redis service, and mount the current directory inside the container so we can
work on code without having to rebuild the image.
- `redis`, which uses the public image
[redis](https://registry.hub.docker.com/_/redis/), which gets pulled from the
Docker Hub registry.
#### web
* Builds from the `Dockerfile` in the current directory.
* Forwards the exposed port 5000 on the container to port 5000 on the host machine.
* Connects the web container to the Redis service via a link.
* Mounts the current directory on the host to `/code` inside the container allowing you to modify the code without having to rebuild the image.
#### redis
* Uses the public [Redis](https://registry.hub.docker.com/_/redis/) image which gets pulled from the Docker Hub registry.
### Build and run your app with Compose
Now, when you run `docker-compose up`, Compose will pull a Redis image, build an
image for your code, and start everything up:
Now, when you run `docker-compose up`, Compose will pull a Redis image, build an image for your code, and start everything up:
$ docker-compose up
Pulling image redis...
@@ -155,13 +159,21 @@ image for your code, and start everything up:
Starting composetest_web_1...
redis_1 | [8] 02 Jan 18:43:35.576 # Server started, Redis version 2.8.3
web_1 | * Running on http://0.0.0.0:5000/
web_1 | * Restarting with stat
The web app should now be listening on port 5000 on your Docker daemon host (if
you're using Boot2docker, `boot2docker ip` will tell you its address).
If you're using [Docker Machine](https://docs.docker.com/machine), then `docker-machine ip MACHINE_VM` will tell you its address and you can open `http://MACHINE_VM_IP:5000` in a browser.
If you're not using Boot2docker and are on linux, then the web app should now be listening on port 5000 on your Docker daemon host. If http://0.0.0.0:5000 doesn't resolve, you can also try localhost:5000.
You should get a message in your browser saying:
`Hello World! I have been seen 1 times.`
Refreshing the page will increment the number.
If you want to run your services in the background, you can pass the `-d` flag
(for daemon mode) to `docker-compose up` and use `docker-compose ps` to see what
is currently running:
(for "detached" mode) to `docker-compose up` and use `docker-compose ps` to
see what is currently running:
$ docker-compose up -d
Starting composetest_redis_1...
@@ -178,7 +190,7 @@ services. For example, to see what environment variables are available to the
$ docker-compose run web env
See `docker-compose --help` to see other available commands.
See `docker-compose --help` to see other available commands. You can also install [command completion](completion.md) for the bash and zsh shell, which will also show you available commands.
If you started Compose with `docker-compose up -d`, you'll probably want to stop
your services once you've finished with them:
@@ -191,3 +203,31 @@ At this point, you have seen the basics of how Compose works.
[Rails](rails.md), or [Wordpress](wordpress.md).
- See the reference guides for complete details on the [commands](cli.md), the
[configuration file](yml.md) and [environment variables](env.md).
## Release Notes
### Version 1.2.0 (April 7, 2015)
For complete information on this release, see the [1.2.0 Milestone project page](https://github.com/docker/compose/wiki/1.2.0-Milestone-Project-Page).
In addition to bug fixes and refinements, this release adds the following:
* The `extends` keyword, which adds the ability to extend services by sharing common configurations. For details, see
[PR #1088](https://github.com/docker/compose/pull/1088).
* Better integration with Swarm. Swarm will now schedule inter-dependent
containers on the same host. For details, see
[PR #972](https://github.com/docker/compose/pull/972).
## Getting help
Docker Compose is still in its infancy and under active development. If you need
help, would like to contribute, or simply want to talk about the project with
like-minded individuals, we have a number of open channels for communication.
* To report bugs or file feature requests: please use the [issue tracker on Github](https://github.com/docker/compose/issues).
* To talk about the project with people in real time: please join the `#docker-compose` channel on IRC.
* To contribute code or documentation changes: please submit a [pull request on Github](https://github.com/docker/compose/pulls).
For more information and resources, please visit the [Getting Help project page](https://docs.docker.com/project/get-help/).

View File

@@ -1,42 +1,103 @@
page_title: Installing Compose
page_description: How to intall Docker Compose
page_keywords: compose, orchestration, install, installation, docker, documentation
<!--[metadata]>
+++
title = "Docker Compose"
description = "How to install Docker Compose"
keywords = ["compose, orchestration, install, installation, docker, documentation"]
[menu.main]
parent="mn_install"
weight=4
+++
<![end-metadata]-->
## Installing Compose
# Install Docker Compose
To install Compose, you'll need to install Docker first. You'll then install
Compose with a `curl` command.
You can run Compose on OS X and 64-bit Linux. It is currently not supported on
the Windows operating system. To install Compose, you'll need to install Docker
first.
### Install Docker
Depending on how your system is configured, you may require `sudo` access to
install Compose. If your system requires `sudo`, you will receive "Permission
denied" errors when installing Compose. If this is the case for you, preface the
install commands with `sudo` to install.
First, install Docker version 1.3 or greater:
To install Compose, do the following:
- [Instructions for Mac OS X](http://docs.docker.com/installation/mac/)
- [Instructions for Ubuntu](http://docs.docker.com/installation/ubuntulinux/)
- [Instructions for other systems](http://docs.docker.com/installation/)
1. Install Docker Engine version 1.7.1 or greater:
### Install Compose
* <a href="https://docs.docker.com/installation/mac/" target="_blank">Mac OS X installation</a> (installs both Engine and Compose)
* <a href="https://docs.docker.com/installation/ubuntulinux/" target="_blank">Ubuntu installation</a>
* <a href="https://docs.docker.com/installation/" target="_blank">other system installations</a>
2. Mac OS X users are done installing. Others should continue to the next step.
3. Go to the <a href="https://github.com/docker/compose/releases" target="_blank">repository release page</a>.
To install Compose, run the following commands:
4. Enter the `curl` command in your termial.
curl -L https://github.com/docker/compose/releases/download/1.2.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
The command has the following format:
Optionally, you can also install [command completion](completion.md) for the
bash shell.
curl -L https://github.com/docker/compose/releases/download/VERSION_NUM/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
If you have problems installing with `curl`, you can use `pip` instead: `pip install -U docker-compose`
4. Apply executable permissions to the binary:
Compose is available for OS X and 64-bit Linux. If you're on another platform,
Compose can also be installed as a Python package:
$ chmod +x /usr/local/bin/docker-compose
$ sudo pip install -U docker-compose
5. Optionally, install [command completion](completion.md) for the
`bash` and `zsh` shell.
No further steps are required; Compose should now be successfully installed.
You can test the installation by running `docker-compose --version`.
6. Test the installation.
## Compose documentation
$ docker-compose --version
docker-compose version: 1.4.1
- [User guide](index.md)
## Upgrading
If you're upgrading from Compose 1.2 or earlier, you'll need to remove or migrate
your existing containers after upgrading Compose. This is because, as of version
1.3, Compose uses Docker labels to keep track of containers, and so they need to
be recreated with labels added.
If Compose detects containers that were created without labels, it will refuse
to run so that you don't end up with two sets of them. If you want to keep using
your existing containers (for example, because they have data volumes you want
to preserve) you can migrate them with the following command:
$ docker-compose migrate-to-labels
Alternatively, if you're not worried about keeping them, you can remove them &endash;
Compose will just create new ones.
$ docker rm -f -v myapp_web_1 myapp_db_1 ...
## Uninstallation
To uninstall Docker Compose if you installed using `curl`:
$ rm /usr/local/bin/docker-compose
To uninstall Docker Compose if you installed using `pip`:
$ pip uninstall docker-compose
>**Note**: If you get a "Permission denied" error using either of the above
>methods, you probably do not have the proper permissions to remove
>`docker-compose`. To force the removal, prepend `sudo` to either of the above
>commands and run again.
## Where to go next
- [User guide](/)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)

View File

@@ -1,10 +0,0 @@
- ['compose/index.md', 'User Guide', 'Docker Compose' ]
- ['compose/install.md', 'Installation', 'Docker Compose']
- ['compose/cli.md', 'Reference', 'Compose command line']
- ['compose/yml.md', 'Reference', 'Compose yml']
- ['compose/env.md', 'Reference', 'Compose ENV variables']
- ['compose/completion.md', 'Reference', 'Compose commandline completion']
- ['compose/django.md', 'Examples', 'Getting started with Compose and Django']
- ['compose/rails.md', 'Examples', 'Getting started with Compose and Rails']
- ['compose/wordpress.md', 'Examples', 'Getting started with Compose and Wordpress']

61
docs/pre-process.sh Executable file
View File

@@ -0,0 +1,61 @@
#!/bin/bash -e
# Populate an array with just docker dirs and one with content dirs
docker_dir=(`ls -d /docs/content/docker/*`)
content_dir=(`ls -d /docs/content/*`)
# Loop content not of docker/
#
# Sed to process GitHub Markdown
# 1-2 Remove comment code from metadata block
# 3 Remove .md extension from link text
# 4 Change ](/ to ](/project/ in links
# 5 Change ](word) to ](/project/word)
# 6 Change ](../../ to ](/project/
# 7 Change ](../ to ](/project/word)
#
for i in "${content_dir[@]}"
do
:
case $i in
"/docs/content/windows")
;;
"/docs/content/mac")
;;
"/docs/content/linux")
;;
"/docs/content/docker")
y=${i##*/}
find $i -type f -name "*.md" -exec sed -i.old \
-e '/^<!.*metadata]>/g' \
-e '/^<!.*end-metadata.*>/g' {} \;
;;
*)
y=${i##*/}
find $i -type f -name "*.md" -exec sed -i.old \
-e '/^<!.*metadata]>/g' \
-e '/^<!.*end-metadata.*>/g' \
-e 's/\(\]\)\([(]\)\(\/\)/\1\2\/'$y'\//g' \
-e 's/\(\][(]\)\([A-z].*\)\(\.md\)/\1\/'$y'\/\2/g' \
-e 's/\([(]\)\(.*\)\(\.md\)/\1\2/g' \
-e 's/\(\][(]\)\(\.\/\)/\1\/'$y'\//g' \
-e 's/\(\][(]\)\(\.\.\/\.\.\/\)/\1\/'$y'\//g' \
-e 's/\(\][(]\)\(\.\.\/\)/\1\/'$y'\//g' {} \;
;;
esac
done
#
# Move docker directories to content
#
for i in "${docker_dir[@]}"
do
:
if [ -d $i ]
then
mv $i /docs/content/
fi
done
rm -rf /docs/content/docker

96
docs/production.md Normal file
View File

@@ -0,0 +1,96 @@
<!--[metadata]>
+++
title = "Using Compose in production"
description = "Guide to using Docker Compose in production"
keywords = ["documentation, docs, docker, compose, orchestration, containers, production"]
[menu.main]
parent="smn_workw_compose"
weight=1
+++
<![end-metadata]-->
## Using Compose in production
While **Compose is not yet considered production-ready**, if you'd like to experiment and learn more about using it in production deployments, this guide
can help.
The project is actively working towards becoming
production-ready; to learn more about the progress being made, check out the
[roadmap](https://github.com/docker/compose/blob/master/ROADMAP.md) for details
on how it's coming along and what still needs to be done.
When deploying to production, you'll almost certainly want to make changes to
your app configuration that are more appropriate to a live environment. These
changes may include:
- Removing any volume bindings for application code, so that code stays inside
the container and can't be changed from outside
- Binding to different ports on the host
- Setting environment variables differently (e.g., to decrease the verbosity of
logging, or to enable email sending)
- Specifying a restart policy (e.g., `restart: always`) to avoid downtime
- Adding extra services (e.g., a log aggregator)
For this reason, you'll probably want to define a separate Compose file, say
`production.yml`, which specifies production-appropriate configuration.
> **Note:** The [extends](extends.md) keyword is useful for maintaining multiple
> Compose files which re-use common services without having to manually copy and
> paste.
Once you've got an alternate configuration file, make Compose use it
by setting the `COMPOSE_FILE` environment variable:
$ COMPOSE_FILE=production.yml
$ docker-compose up -d
> **Note:** You can also use the file for a one-off command without setting
> an environment variable. You do this by passing the `-f` flag, e.g.,
> `docker-compose -f production.yml up -d`.
### Deploying changes
When you make changes to your app code, you'll need to rebuild your image and
recreate your app's containers. To redeploy a service called
`web`, you would use:
$ docker-compose build web
$ docker-compose up --no-deps -d web
This will first rebuild the image for `web` and then stop, destroy, and recreate
*just* the `web` service. The `--no-deps` flag prevents Compose from also
recreating any services which `web` depends on.
### Running Compose on a single server
You can use Compose to deploy an app to a remote Docker host by setting the
`DOCKER_HOST`, `DOCKER_TLS_VERIFY`, and `DOCKER_CERT_PATH` environment variables
appropriately. For tasks like this,
[Docker Machine](https://docs.docker.com/machine) makes managing local and
remote Docker hosts very easy, and is recommended even if you're not deploying
remotely.
Once you've set up your environment variables, all the normal `docker-compose`
commands will work with no further configuration.
### Running Compose on a Swarm cluster
[Docker Swarm](https://docs.docker.com/swarm), a Docker-native clustering
system, exposes the same API as a single Docker host, which means you can use
Compose against a Swarm instance and run your apps across multiple hosts.
Compose/Swarm integration is still in the experimental stage, and Swarm is still
in beta, but if you'd like to explore and experiment, check out the
[integration guide](https://github.com/docker/compose/blob/master/SWARM.md).
## Compose documentation
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)
- [Compose command line completion](completion.md)

View File

@@ -1,10 +1,15 @@
page_title: Quickstart Guide: Compose and Rails
page_description: Getting started with Docker Compose and Rails
page_keywords: documentation, docs, docker, compose, orchestration, containers,
rails
<!--[metadata]>
+++
title = "Quickstart Guide: Compose and Rails"
description = "Getting started with Docker Compose and Rails"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
weight=5
+++
<![end-metadata]-->
## Getting started with Compose and Rails
## Quickstart Guide: Compose and Rails
This Quickstart guide will show you how to use Compose to set up and run a Rails/PostgreSQL app. Before starting, you'll need to have [Compose installed](install.md).
@@ -35,8 +40,6 @@ Finally, `docker-compose.yml` is where the magic happens. This file describes th
db:
image: postgres
ports:
- "5432"
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
@@ -114,13 +117,16 @@ Finally, you need to create the database. In another terminal, run:
$ docker-compose run web rake db:create
That's it. Your app should now be running on port 3000 on your Docker daemon (if
you're using Boot2docker, `boot2docker ip` will tell you its address).
That's it. Your app should now be running on port 3000 on your Docker daemon. If you're using [Docker Machine](https://docs.docker.com/machine), then `docker-machine ip MACHINE_VM` returns the Docker host IP address.
## More Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [User guide](index.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)

23
docs/reference/build.md Normal file
View File

@@ -0,0 +1,23 @@
<!--[metadata]>
+++
title = "build"
description = "build"
keywords = ["fig, composition, compose, docker, orchestration, cli, build"]
[menu.main]
identifier="build.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# build
```
Usage: build [options] [SERVICE...]
Options:
--no-cache Do not use cache when building the image.
```
Services are built once and then tagged as `project_service`, e.g.,
`composetest_db`. If you change a service's Dockerfile or the contents of its
build directory, run `docker-compose build` to rebuild it.

View File

@@ -0,0 +1,55 @@
<!--[metadata]>
+++
title = "docker-compose"
description = "docker-compose Command Binary"
keywords = ["fig, composition, compose, docker, orchestration, cli, docker-compose"]
[menu.main]
parent = "smn_compose_cli"
weight=-2
+++
<![end-metadata]-->
# docker-compose Command
```
Usage:
docker-compose [options] [COMMAND] [ARGS...]
docker-compose -h|--help
Options:
-f, --file FILE Specify an alternate compose file (default: docker-compose.yml)
-p, --project-name NAME Specify an alternate project name (default: directory name)
--verbose Show more output
-v, --version Print version and exit
Commands:
build Build or rebuild services
help Get help on a command
kill Kill containers
logs View output from containers
port Print the public port for a port binding
ps List containers
pull Pulls service images
restart Restart services
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
up Create and start containers
migrate-to-labels Recreate containers to add labels
```
The Docker Compose binary. You use this command to build and manage multiple services in Docker containers.
Use the `-f` flag to specify the location of a Compose configuration file. This
flag is optional. If you don't provide this flag. Compose looks for a file named
`docker-compose.yml` in the working directory. If the file is not found,
Compose looks in each parent directory successively, until it finds the file.
Use a `-` as the filename to read configuration file from stdin. When stdin is
used all paths in the configuration are relative to the current working
directory.
Each configuration can has a project name. If you supply a `-p` flag, you can specify a project name. If you don't specify the flag, Compose uses the current directory name.

18
docs/reference/help.md Normal file
View File

@@ -0,0 +1,18 @@
<!--[metadata]>
+++
title = "help"
description = "help"
keywords = ["fig, composition, compose, docker, orchestration, cli, help"]
[menu.main]
identifier="help.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# help
```
Usage: help COMMAND
```
Displays help and usage instructions for a command.

29
docs/reference/index.md Normal file
View File

@@ -0,0 +1,29 @@
<!--[metadata]>
+++
title = "Compose CLI reference"
description = "Compose CLI reference"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
[menu.main]
identifier = "smn_compose_cli"
parent = "smn_compose_ref"
+++
<![end-metadata]-->
## Compose CLI reference
The following pages describe the usage information for the [docker-compose](/reference/docker-compose.md) subcommands. You can also see this information by running `docker-compose [SUBCOMMAND] --help` from the command line.
* [build](/reference/reference/build.md)
* [help](/reference/help.md)
* [kill](/reference/kill.md)
* [ps](/reference/ps.md)
* [restart](/reference/restart.md)
* [run](/reference/run.md)
* [start](/reference/start.md)
* [up](/reference/up.md)
* [logs](/reference/logs.md)
* [port](/reference/port.md)
* [pull](/reference/pull.md)
* [rm](/reference/rm.md)
* [scale](/reference/scale.md)
* [stop](/reference/stop.md)

24
docs/reference/kill.md Normal file
View File

@@ -0,0 +1,24 @@
<!--[metadata]>
+++
title = "kill"
description = "Forces running containers to stop."
keywords = ["fig, composition, compose, docker, orchestration, cli, kill"]
[menu.main]
identifier="kill.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# kill
```
Usage: kill [options] [SERVICE...]
Options:
-s SIGNAL SIGNAL to send to the container. Default signal is SIGKILL.
```
Forces running containers to stop by sending a `SIGKILL` signal. Optionally the
signal can be passed, for example:
$ docker-compose kill -s SIGINT

21
docs/reference/logs.md Normal file
View File

@@ -0,0 +1,21 @@
<!--[metadata]>
+++
title = "logs"
description = "Displays log output from services."
keywords = ["fig, composition, compose, docker, orchestration, cli, logs"]
[menu.main]
identifier="logs.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# logs
```
Usage: logs [options] [SERVICE...]
Options:
--no-color Produce monochrome output.
```
Displays log output from services.

View File

@@ -0,0 +1,62 @@
<!--[metadata]>
+++
title = "Introduction to the CLI"
description = "Introduction to the CLI"
keywords = ["fig, composition, compose, docker, orchestration, cli, reference"]
[menu.main]
parent = "smn_compose_cli"
weight=-2
+++
<![end-metadata]-->
# Introduction to the CLI
This section describes the subcommands you can use with the `docker-compose` command. You can run subcommand against one or more services. To run against a specific service, you supply the service name from your compose configuration. If you do not specify the service name, the command runs against all the services in your configuration.
## Environment Variables
Several environment variables are available for you to configure the Docker Compose command-line behaviour.
Variables starting with `DOCKER_` are the same as those used to configure the
Docker command-line client. If you're using `docker-machine`, then the `eval "$(docker-machine env my-docker-vm)"` command should set them to their correct values. (In this example, `my-docker-vm` is the name of a machine you created.)
### COMPOSE\_PROJECT\_NAME
Sets the project name. This value is prepended along with the service name to the container container on start up. For example, if you project name is `myapp` and it includes two services `db` and `web` then compose starts containers named `myapp_db_1` and `myapp_web_1` respectively.
Setting this is optional. If you do not set this, the `COMPOSE_PROJECT_NAME` defaults to the `basename` of the current working directory.
### COMPOSE\_FILE
Specify the file containing the compose configuration. If not provided, Compose looks for a file named `docker-compose.yml` in the current directory and then each parent directory in succession until a file by that name is found.
### DOCKER\_HOST
Sets the URL of the `docker` daemon. As with the Docker client, defaults to `unix:///var/run/docker.sock`.
### DOCKER\_TLS\_VERIFY
When set to anything other than an empty string, enables TLS communication with
the `docker` daemon.
### DOCKER\_CERT\_PATH
Configures the path to the `ca.pem`, `cert.pem`, and `key.pem` files used for TLS verification. Defaults to `~/.docker`.
## Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)
- [Compose command line completion](completion.md)

23
docs/reference/port.md Normal file
View File

@@ -0,0 +1,23 @@
<!--[metadata]>
+++
title = "port"
description = "Prints the public port for a port binding.s"
keywords = ["fig, composition, compose, docker, orchestration, cli, port"]
[menu.main]
identifier="port.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# port
```
Usage: port [options] SERVICE PRIVATE_PORT
Options:
--protocol=proto tcp or udp [default: tcp]
--index=index index of the container if there are multiple
instances of a service [default: 1]
```
Prints the public port for a port binding.

21
docs/reference/ps.md Normal file
View File

@@ -0,0 +1,21 @@
<!--[metadata]>
+++
title = "ps"
description = "Lists containers."
keywords = ["fig, composition, compose, docker, orchestration, cli, ps"]
[menu.main]
identifier="ps.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# ps
```
Usage: ps [options] [SERVICE...]
Options:
-q Only display IDs
```
Lists containers.

18
docs/reference/pull.md Normal file
View File

@@ -0,0 +1,18 @@
<!--[metadata]>
+++
title = "pull"
description = "Pulls service images."
keywords = ["fig, composition, compose, docker, orchestration, cli, pull"]
[menu.main]
identifier="pull.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# pull
```
Usage: pull [options] [SERVICE...]
```
Pulls service images.

21
docs/reference/restart.md Normal file
View File

@@ -0,0 +1,21 @@
<!--[metadata]>
+++
title = "restart"
description = "Restarts Docker Compose services."
keywords = ["fig, composition, compose, docker, orchestration, cli, restart"]
[menu.main]
identifier="restart.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# restart
```
Usage: restart [options] [SERVICE...]
Options:
-t, --timeout TIMEOUT Specify a shutdown timeout in seconds. (default: 10)
```
Restarts services.

22
docs/reference/rm.md Normal file
View File

@@ -0,0 +1,22 @@
<!--[metadata]>
+++
title = "rm"
description = "Removes stopped service containers."
keywords = ["fig, composition, compose, docker, orchestration, cli, rm"]
[menu.main]
identifier="rm.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# rm
```
Usage: rm [options] [SERVICE...]
Options:
-f, --force Don't ask to confirm removal
-v Remove volumes associated with containers
```
Removes stopped service containers.

53
docs/reference/run.md Normal file
View File

@@ -0,0 +1,53 @@
<!--[metadata]>
+++
title = "run"
description = "Runs a one-off command on a service."
keywords = ["fig, composition, compose, docker, orchestration, cli, run"]
[menu.main]
identifier="run.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# run
```
Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]
Options:
-d Detached mode: Run container in the background, print
new container name.
--entrypoint CMD Override the entrypoint of the image.
-e KEY=VAL Set an environment variable (can be used multiple times)
-u, --user="" Run as specified username or uid
--no-deps Don't start linked services.
--rm Remove container after run. Ignored in detached mode.
--service-ports Run command with the service's ports enabled and mapped to the host.
-T Disable pseudo-tty allocation. By default `docker-compose run` allocates a TTY.
```
Runs a one-time command against a service. For example, the following command starts the `web` service and runs `bash` as its command.
$ docker-compose run web bash
Commands you use with `run` start in new containers with the same configuration as defined by the service' configuration. This means the container has the same volumes, links, as defined in the configuration file. There two differences though.
First, the command passed by `run` overrides the command defined in the service configuration. For example, if the `web` service configuration is started with `bash`, then `docker-compose run web python app.py` overrides it with `python app.py`.
The second difference is the `docker-compose run` command does not create any of the ports specified in the service configuration. This prevents the port collisions with already open ports. If you *do want* the service's ports created and mapped to the host, specify the `--service-ports` flag:
$ docker-compose run --service-ports web python manage.py shell
If you start a service configured with links, the `run` command first checks to see if the linked service is running and starts the service if it is stopped. Once all the linked services are running, the `run` executes the command you passed it. So, for example, you could run:
$ docker-compose run db psql -h db -U docker
This would open up an interactive PostgreSQL shell for the linked `db` container.
If you do not want the `run` command to start linked containers, specify the `--no-deps` flag:
$ docker-compose run --no-deps web python manage.py shell

21
docs/reference/scale.md Normal file
View File

@@ -0,0 +1,21 @@
<!--[metadata]>
+++
title = "scale"
description = "Sets the number of containers to run for a service."
keywords = ["fig, composition, compose, docker, orchestration, cli, scale"]
[menu.main]
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# scale
```
Usage: scale [SERVICE=NUM...]
```
Sets the number of containers to run for a service.
Numbers are specified as arguments in the form `service=num`. For example:
$ docker-compose scale web=2 worker=3

18
docs/reference/start.md Normal file
View File

@@ -0,0 +1,18 @@
<!--[metadata]>
+++
title = "start"
description = "Starts existing containers for a service."
keywords = ["fig, composition, compose, docker, orchestration, cli, start"]
[menu.main]
identifier="start.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# start
```
Usage: start [SERVICE...]
```
Starts existing containers for a service.

22
docs/reference/stop.md Normal file
View File

@@ -0,0 +1,22 @@
<!--[metadata]>
+++
title = "stop"
description = "Stops running containers without removing them. "
keywords = ["fig, composition, compose, docker, orchestration, cli, stop"]
[menu.main]
identifier="stop.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# stop
```
Usage: stop [options] [SERVICE...]
Options:
-t, --timeout TIMEOUT Specify a shutdown timeout in seconds (default: 10).
```
Stops running containers without removing them. They can be started again with
`docker-compose start`.

47
docs/reference/up.md Normal file
View File

@@ -0,0 +1,47 @@
<!--[metadata]>
+++
title = "up"
description = "Builds, (re)creates, starts, and attaches to containers for a service."
keywords = ["fig, composition, compose, docker, orchestration, cli, up"]
[menu.main]
identifier="up.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# up
```
Usage: up [options] [SERVICE...]
Options:
-d Detached mode: Run containers in the background,
print new container names.
--no-color Produce monochrome output.
--no-deps Don't start linked services.
--force-recreate Recreate containers even if their configuration and
image haven't changed. Incompatible with --no-recreate.
--no-recreate If containers already exist, don't recreate them.
Incompatible with --force-recreate.
--no-build Don't build an image, even if it's missing
-t, --timeout TIMEOUT Use this timeout in seconds for container shutdown
when attached or when containers are already
running. (default: 10)
```
Builds, (re)creates, starts, and attaches to containers for a service.
Unless they are already running, this command also starts any linked services.
The `docker-compose up` command aggregates the output of each container. When
the command exits, all containers are stopped. Running `docker-compose up -d`
starts the containers in the background and leaves them running.
If there are existing containers for a service, and the service's configuration
or image was changed after the container's creation, `docker-compose up` picks
up the changes by stopping and recreating the containers (preserving mounted
volumes). To prevent Compose from picking up changes, use the `--no-recreate`
flag.
If you want to force Compose to stop and recreate all containers, use the
`--force-recreate` flag.

View File

@@ -1,14 +1,21 @@
page_title: Quickstart Guide: Compose and Wordpress
page_description: Getting started with Docker Compose and Rails
page_keywords: documentation, docs, docker, compose, orchestration, containers,
wordpress
<!--[metadata]>
+++
title = "Quickstart Guide: Compose and Wordpress"
description = "Getting started with Compose and Wordpress"
keywords = ["documentation, docs, docker, compose, orchestration, containers"]
[menu.main]
parent="smn_workw_compose"
weight=6
+++
<![end-metadata]-->
## Getting started with Compose and Wordpress
# Quickstart Guide: Compose and Wordpress
You can use Compose to easily run Wordpress in an isolated environment built
with Docker containers.
### Define the project
## Define the project
First, [Install Compose](install.md) and then download Wordpress into the
current directory:
@@ -25,10 +32,8 @@ Dockerfiles, see the
[Dockerfile reference](http://docs.docker.com/reference/builder/). In this case,
your Dockerfile should be:
```
FROM orchardup/php5
ADD . /code
```
FROM orchardup/php5
ADD . /code
This tells Docker how to build an image defining a container that contains PHP
and Wordpress.
@@ -36,86 +41,82 @@ and Wordpress.
Next you'll create a `docker-compose.yml` file that will start your web service
and a separate MySQL instance:
```
web:
build: .
command: php -S 0.0.0.0:8000 -t /code
ports:
- "8000:8000"
links:
- db
volumes:
- .:/code
db:
image: orchardup/mysql
environment:
MYSQL_DATABASE: wordpress
```
web:
build: .
command: php -S 0.0.0.0:8000 -t /code
ports:
- "8000:8000"
links:
- db
volumes:
- .:/code
db:
image: orchardup/mysql
environment:
MYSQL_DATABASE: wordpress
Two supporting files are needed to get this working - first, `wp-config.php` is
the standard Wordpress config file with a single change to point the database
configuration at the `db` container:
```
<?php
define('DB_NAME', 'wordpress');
define('DB_USER', 'root');
define('DB_PASSWORD', '');
define('DB_HOST', "db:3306");
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
<?php
define('DB_NAME', 'wordpress');
define('DB_USER', 'root');
define('DB_PASSWORD', '');
define('DB_HOST', "db:3306");
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
define('AUTH_KEY', 'put your unique phrase here');
define('SECURE_AUTH_KEY', 'put your unique phrase here');
define('LOGGED_IN_KEY', 'put your unique phrase here');
define('NONCE_KEY', 'put your unique phrase here');
define('AUTH_SALT', 'put your unique phrase here');
define('SECURE_AUTH_SALT', 'put your unique phrase here');
define('LOGGED_IN_SALT', 'put your unique phrase here');
define('NONCE_SALT', 'put your unique phrase here');
define('AUTH_KEY', 'put your unique phrase here');
define('SECURE_AUTH_KEY', 'put your unique phrase here');
define('LOGGED_IN_KEY', 'put your unique phrase here');
define('NONCE_KEY', 'put your unique phrase here');
define('AUTH_SALT', 'put your unique phrase here');
define('SECURE_AUTH_SALT', 'put your unique phrase here');
define('LOGGED_IN_SALT', 'put your unique phrase here');
define('NONCE_SALT', 'put your unique phrase here');
$table_prefix = 'wp_';
define('WPLANG', '');
define('WP_DEBUG', false);
$table_prefix = 'wp_';
define('WPLANG', '');
define('WP_DEBUG', false);
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');
require_once(ABSPATH . 'wp-settings.php');
```
require_once(ABSPATH . 'wp-settings.php');
Second, `router.php` tells PHP's built-in web server how to run Wordpress:
```
<?php
<?php
$root = $_SERVER['DOCUMENT_ROOT'];
chdir($root);
$path = '/'.ltrim(parse_url($_SERVER['REQUEST_URI'])['path'],'/');
set_include_path(get_include_path().':'.__DIR__);
if(file_exists($root.$path))
{
if(is_dir($root.$path) && substr($path,strlen($path) - 1, 1) !== '/')
$path = rtrim($path,'/').'/index.php';
if(strpos($path,'.php') === false) return false;
else {
chdir(dirname($root.$path));
require_once $root.$path;
}
}else include_once 'index.php';
$root = $_SERVER['DOCUMENT_ROOT'];
chdir($root);
$path = '/'.ltrim(parse_url($_SERVER['REQUEST_URI'])['path'],'/');
set_include_path(get_include_path().':'.__DIR__);
if(file_exists($root.$path))
{
if(is_dir($root.$path) && substr($path,strlen($path) - 1, 1) !== '/')
$path = rtrim($path,'/').'/index.php';
if(strpos($path,'.php') === false) return false;
else {
chdir(dirname($root.$path));
require_once $root.$path;
}
}else include_once 'index.php';
```
### Build the project
With those four files in place, run `docker-compose up` inside your Wordpress
directory and it'll pull and build the needed images, and then start the web and
database containers. You'll then be able to visit Wordpress at port 8000 on your
Docker daemon (if you're using Boot2docker, `boot2docker ip` will tell you its
address).
database containers. If you're using [Docker Machine](https://docs.docker.com/machine), then `docker-machine ip MACHINE_VM` gives you the machine address and you can open `http://MACHINE_VM_IP:8000` in a browser.
## More Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [User guide](index.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Yaml file reference](yml.md)
- [Compose environment variables](env.md)

View File

@@ -1,10 +1,13 @@
---
layout: default
title: docker-compose.yml reference
page_title: docker-compose.yml reference
page_description: docker-compose.yml reference
page_keywords: fig, composition, compose, docker
---
<!--[metadata]>
+++
title = "docker-compose.yml reference"
description = "docker-compose.yml reference"
keywords = ["fig, composition, compose, docker"]
[menu.main]
parent="smn_compose_ref"
+++
<![end-metadata]-->
# docker-compose.yml reference
@@ -21,31 +24,33 @@ specify them again in `docker-compose.yml`.
Tag or partial image ID. Can be local or remote - Compose will attempt to
pull if it doesn't exist locally.
```
image: ubuntu
image: orchardup/postgresql
image: a4bc65fd
```
image: ubuntu
image: orchardup/postgresql
image: a4bc65fd
### build
Path to a directory containing a Dockerfile. When the value supplied is a
relative path, it is interpreted as relative to the location of the yml file
Path to a directory containing a Dockerfile. When the value supplied is a
relative path, it is interpreted as relative to the location of the yml file
itself. This directory is also the build context that is sent to the Docker daemon.
Compose will build and tag it with a generated name, and use that image thereafter.
```
build: /path/to/build/dir
```
build: /path/to/build/dir
### dockerfile
Alternate Dockerfile.
Compose will use an alternate file to build with.
dockerfile: Dockerfile-alternate
### command
Override the default command.
```
command: bundle exec thin -p 3000
```
command: bundle exec thin -p 3000
<a name="links"></a>
### links
@@ -54,21 +59,17 @@ Link to containers in another service. Either specify both the service name and
the link alias (`SERVICE:ALIAS`), or just the service name (which will also be
used for the alias).
```
links:
- db
- db:database
- redis
```
links:
- db
- db:database
- redis
An entry with the alias' name will be created in `/etc/hosts` inside containers
for this service, e.g:
```
172.17.2.186 db
172.17.2.186 database
172.17.2.187 redis
```
172.17.2.186 db
172.17.2.186 database
172.17.2.187 redis
Environment variables will also be created - see the [environment variable
reference](env.md) for details.
@@ -80,12 +81,23 @@ of Compose, especially for containers that provide shared or common services.
`external_links` follow semantics similar to `links` when specifying both the
container name and the link alias (`CONTAINER:ALIAS`).
```
external_links:
- redis_1
- project_db_1:mysql
- project_db_1:postgresql
```
external_links:
- redis_1
- project_db_1:mysql
- project_db_1:postgresql
### extra_hosts
Add hostname mappings. Use the same values as the docker client `--add-host` parameter.
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"
An entry with the ip address and hostname will be created in `/etc/hosts` inside containers for this service, e.g:
162.242.195.82 somehost
50.31.209.229 otherhost
### ports
@@ -97,46 +109,45 @@ port (a random host port will be chosen).
> parse numbers in the format `xx:yy` as sexagesimal (base 60). For this reason,
> we recommend always explicitly specifying your port mappings as strings.
```
ports:
- "3000"
- "8000:8000"
- "49100:22"
- "127.0.0.1:8001:8001"
```
ports:
- "3000"
- "8000:8000"
- "49100:22"
- "127.0.0.1:8001:8001"
### expose
Expose ports without publishing them to the host machine - they'll only be
accessible to linked services. Only the internal port can be specified.
```
expose:
- "3000"
- "8000"
```
expose:
- "3000"
- "8000"
### volumes
Mount paths as volumes, optionally specifying a path on the host machine
(`HOST:CONTAINER`), or an access mode (`HOST:CONTAINER:ro`).
```
volumes:
- /var/lib/mysql
- cache/:/tmp/cache
- ~/configs:/etc/configs/:ro
```
volumes:
- /var/lib/mysql
- ./cache:/tmp/cache
- ~/configs:/etc/configs/:ro
You can mount a relative path on the host, which will expand relative to
the directory of the Compose configuration file being used. Relative paths
should always begin with `.` or `..`.
> Note: No path expansion will be done if you have also specified a
> `volume_driver`.
### volumes_from
Mount all of the volumes from another service or container.
```
volumes_from:
- service_name
- container_name
```
volumes_from:
- service_name
- container_name
### environment
@@ -145,15 +156,13 @@ Add environment variables. You can use either an array or a dictionary.
Environment variables with only a key are resolved to their values on the
machine Compose is running on, which can be helpful for secret or host-specific values.
```
environment:
RACK_ENV: development
SESSION_SECRET:
environment:
RACK_ENV: development
SESSION_SECRET:
environment:
- RACK_ENV=development
- SESSION_SECRET
```
environment:
- RACK_ENV=development
- SESSION_SECRET
### env_file
@@ -164,18 +173,18 @@ If you have specified a Compose file with `docker-compose -f FILE`, paths in
Environment variables specified in `environment` override these values.
```
env_file: .env
env_file: .env
env_file:
- ./common.env
- ./apps/web.env
- /opt/secrets.env
```
env_file:
- ./common.env
- ./apps/web.env
- /opt/secrets.env
```
RACK_ENV: development
```
Compose expects each line in an env file to be in `VAR=VAL` format. Lines
beginning with `#` (i.e. comments) are ignored, as are blank lines.
# Set Rails/Rack environment
RACK_ENV=development
### extends
@@ -188,147 +197,186 @@ Here's a simple example. Suppose we have 2 files - **common.yml** and
**common.yml**
```
webapp:
build: ./webapp
environment:
- DEBUG=false
- SEND_EMAILS=false
```
webapp:
build: ./webapp
environment:
- DEBUG=false
- SEND_EMAILS=false
**development.yml**
```
web:
extends:
file: common.yml
service: webapp
ports:
- "8000:8000"
links:
- db
environment:
- DEBUG=true
db:
image: postgres
```
web:
extends:
file: common.yml
service: webapp
ports:
- "8000:8000"
links:
- db
environment:
- DEBUG=true
db:
image: postgres
Here, the `web` service in **development.yml** inherits the configuration of
the `webapp` service in **common.yml** - the `build` and `environment` keys -
and adds `ports` and `links` configuration. It overrides one of the defined
environment variables (DEBUG) with a new value, and the other one
(SEND_EMAILS) is left untouched. It's exactly as if you defined `web` like
this:
(SEND_EMAILS) is left untouched.
```yaml
web:
build: ./webapp
ports:
- "8000:8000"
links:
- db
environment:
- DEBUG=true
- SEND_EMAILS=false
```
The `file` key is optional, if it is not set then Compose will look for the
service within the current file.
The `extends` option is great for sharing configuration between different
apps, or for configuring the same app differently for different environments.
You could write a new file for a staging environment, **staging.yml**, which
binds to a different port and doesn't turn on debugging:
For more on `extends`, see the [tutorial](extends.md#example) and
[reference](extends.md#reference).
```
web:
extends:
file: common.yml
service: webapp
ports:
- "80:8000"
links:
- db
db:
image: postgres
```
### labels
> **Note:** When you extend a service, `links` and `volumes_from`
> configuration options are **not** inherited - you will have to define
> those manually each time you extend it.
Add metadata to containers using [Docker labels](http://docs.docker.com/userguide/labels-custom-metadata/). You can use either an array or a dictionary.
It's recommended that you use reverse-DNS notation to prevent your labels from conflicting with those used by other software.
labels:
com.example.description: "Accounting webapp"
com.example.department: "Finance"
com.example.label-with-empty-value: ""
labels:
- "com.example.description=Accounting webapp"
- "com.example.department=Finance"
- "com.example.label-with-empty-value"
### container_name
Specify a custom container name, rather than a generated default name.
container_name: my-web-container
Because Docker container names must be unique, you cannot scale a service
beyond 1 container if you have specified a custom name. Attempting to do so
results in an error.
### log driver
Specify a logging driver for the service's containers, as with the ``--log-driver`` option for docker run ([documented here](http://docs.docker.com/reference/run/#logging-drivers-log-driver)).
Allowed values are currently ``json-file``, ``syslog`` and ``none``. The list will change over time as more drivers are added to the Docker engine.
The default value is json-file.
log_driver: "json-file"
log_driver: "syslog"
log_driver: "none"
Specify logging options with `log_opt` for the logging driver, as with the ``--log-opt`` option for `docker run`.
Logging options are key value pairs. An example of `syslog` options:
log_driver: "syslog"
log_opt:
address: "tcp://192.168.0.42:123"
### net
Networking mode. Use the same values as the docker client `--net` parameter.
```
net: "bridge"
net: "none"
net: "container:[name or id]"
net: "host"
```
net: "bridge"
net: "none"
net: "container:[name or id]"
net: "host"
### pid
pid: "host"
Sets the PID mode to the host PID mode. This turns on sharing between
container and the host operating system the PID address space. Containers
launched with this flag will be able to access and manipulate other
containers in the bare-metal machine's namespace and vise-versa.
### dns
Custom DNS servers. Can be a single value or a list.
```
dns: 8.8.8.8
dns:
- 8.8.8.8
- 9.9.9.9
```
dns: 8.8.8.8
dns:
- 8.8.8.8
- 9.9.9.9
### cap_add, cap_drop
Add or drop container capabilities.
See `man 7 capabilities` for a full list.
```
cap_add:
- ALL
cap_add:
- ALL
cap_drop:
- NET_ADMIN
- SYS_ADMIN
```
cap_drop:
- NET_ADMIN
- SYS_ADMIN
### dns_search
Custom DNS search domains. Can be a single value or a list.
```
dns_search: example.com
dns_search:
- dc1.example.com
- dc2.example.com
```
dns_search: example.com
dns_search:
- dc1.example.com
- dc2.example.com
### working\_dir, entrypoint, user, hostname, domainname, mem\_limit, privileged, restart, stdin\_open, tty, cpu\_shares
### devices
List of device mappings. Uses the same format as the `--device` docker
client create option.
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0"
### security_opt
Override the default labeling scheme for each container.
security_opt:
- label:user:USER
- label:role:ROLE
### working\_dir, entrypoint, user, hostname, domainname, mac\_address, mem\_limit, memswap\_limit, privileged, restart, stdin\_open, tty, cpu\_shares, cpuset, read\_only, volume\_driver
Each of these is a single value, analogous to its
[docker run](https://docs.docker.com/reference/run/) counterpart.
```
cpu_shares: 73
cpu_shares: 73
cpuset: 0,1
working_dir: /code
entrypoint: /code/entrypoint.sh
user: postgresql
working_dir: /code
entrypoint: /code/entrypoint.sh
user: postgresql
hostname: foo
domainname: foo.com
hostname: foo
domainname: foo.com
mem_limit: 1000000000
privileged: true
mac_address: 02:42:ac:11:65:43
restart: always
mem_limit: 1000000000
memswap_limit: 2000000000
privileged: true
stdin_open: true
tty: true
restart: always
stdin_open: true
tty: true
read_only: true
volume_driver: mydriver
```
## Compose documentation
- [User guide](/)
- [Installing Compose](install.md)
- [User guide](index.md)
- [Get started with Django](django.md)
- [Get started with Rails](rails.md)
- [Get started with Wordpress](wordpress.md)
- [Command line reference](cli.md)
- [Compose environment variables](env.md)
- [Compose command line completion](completion.md)

View File

@@ -0,0 +1,183 @@
# Experimental: Compose, Swarm and Multi-Host Networking
The [experimental build of Docker](https://github.com/docker/docker/tree/master/experimental) has an entirely new networking system, which enables secure communication between containers on multiple hosts. In combination with Docker Swarm and Docker Compose, you can now run multi-container apps on multi-host clusters with the same tooling and configuration format you use to develop them locally.
> Note: This functionality is in the experimental stage, and contains some hacks and workarounds which will be removed as it matures.
## Prerequisites
Before you start, youll need to install the experimental build of Docker, and the latest versions of Machine and Compose.
- To install the experimental Docker build on a Linux machine, follow the instructions [here](https://github.com/docker/docker/tree/master/experimental#install-docker-experimental).
- To install the experimental Docker build on a Mac, run these commands:
$ curl -L https://experimental.docker.com/builds/Darwin/x86_64/docker-latest > /usr/local/bin/docker
$ chmod +x /usr/local/bin/docker
- To install Machine, follow the instructions [here](http://docs.docker.com/machine/).
- To install Compose, follow the instructions [here](http://docs.docker.com/compose/install/).
Youll also need a [Docker Hub](https://hub.docker.com/account/signup/) account and a [Digital Ocean](https://www.digitalocean.com/) account.
## Set up a swarm with multi-host networking
Set the `DIGITALOCEAN_ACCESS_TOKEN` environment variable to a valid Digital Ocean API token, which you can generate in the [API panel](https://cloud.digitalocean.com/settings/applications).
DIGITALOCEAN_ACCESS_TOKEN=abc12345
Start a consul server:
docker-machine create -d digitalocean --engine-install-url https://experimental.docker.com consul
docker $(docker-machine config consul) run -d -p 8500:8500 -h consul progrium/consul -server -bootstrap
(In a real world setting youd set up a distributed consul, but thats beyond the scope of this guide!)
Create a Swarm token:
SWARM_TOKEN=$(docker run swarm create)
Create a Swarm master:
docker-machine create -d digitalocean --swarm --swarm-master --swarm-discovery=token://$SWARM_TOKEN --engine-install-url="https://experimental.docker.com" --digitalocean-image "ubuntu-14-10-x64" --engine-opt=default-network=overlay:multihost --engine-label=com.docker.network.driver.overlay.bind_interface=eth0 --engine-opt=kv-store=consul:$(docker-machine ip consul):8500 swarm-0
Create a Swarm node:
docker-machine create -d digitalocean --swarm --swarm-discovery=token://$SWARM_TOKEN --engine-install-url="https://experimental.docker.com" --digitalocean-image "ubuntu-14-10-x64" --engine-opt=default-network=overlay:multihost --engine-label=com.docker.network.driver.overlay.bind_interface=eth0 --engine-opt=kv-store=consul:$(docker-machine ip consul):8500 --engine-label com.docker.network.driver.overlay.neighbor_ip=$(docker-machine ip swarm-0) swarm-1
You can create more Swarm nodes if you want - its best to give them sensible names (swarm-2, swarm-3, etc).
Finally, point Docker at your swarm:
eval "$(docker-machine env --swarm swarm-0)"
## Run containers and get them communicating
Now that youve got a swarm up and running, you can create containers on it just like a single Docker instance:
$ docker run busybox echo hello world
hello world
If you run `docker ps -a`, you can see what node that container was started on by looking at its name (here its swarm-3):
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41f59749737b busybox "echo hello world" 15 seconds ago Exited (0) 13 seconds ago swarm-3/trusting_leakey
As you start more containers, theyll be placed on different nodes across the cluster, thanks to Swarms default “spread” scheduling strategy.
Every container started on this swarm will use the “overlay:multihost” network by default, meaning they can all intercommunicate. Each container gets an IP address on that network, and an `/etc/hosts` file which will be updated on-the-fly with every other containers IP address and name. That means that if you have a running container named foo, other containers can access it at the hostname foo.
Lets verify that multi-host networking is functioning. Start a long-running container:
$ docker run -d --name long-running busybox top
<container id>
If you start a new container and inspect its /etc/hosts file, youll see the long-running container in there:
$ docker run busybox cat /etc/hosts
...
172.21.0.6 long-running
Verify that connectivity works between containers:
$ docker run busybox ping long-running
PING long-running (172.21.0.6): 56 data bytes
64 bytes from 172.21.0.6: seq=0 ttl=64 time=7.975 ms
64 bytes from 172.21.0.6: seq=1 ttl=64 time=1.378 ms
64 bytes from 172.21.0.6: seq=2 ttl=64 time=1.348 ms
^C
--- long-running ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 1.140/2.099/7.975 ms
## Run a Compose application
Heres an example of a simple Python + Redis app using multi-host networking on a swarm.
Create a directory for the app:
$ mkdir composetest
$ cd composetest
Inside this directory, create 2 files.
First, create `app.py` - a simple web app that uses the Flask framework and increments a value in Redis:
from flask import Flask
from redis import Redis
import os
app = Flask(__name__)
redis = Redis(host='composetest_redis_1', port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return 'Hello World! I have been seen %s times.' % redis.get('hits')
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
Note that were connecting to a host called `composetest_redis_1` - this is the name of the Redis container that Compose will start.
Second, create a Dockerfile for the app container:
FROM python:2.7
RUN pip install flask redis
ADD . /code
WORKDIR /code
CMD ["python", "app.py"]
Build the Docker image and push it to the Hub (youll need a Hub account). Replace `<username>` with your Docker Hub username:
$ docker build -t <username>/counter .
$ docker push <username>/counter
Next, create a `docker-compose.yml`, which defines the configuration for the web and redis containers. Once again, replace `<username>` with your Hub username:
web:
image: <username>/counter
ports:
- "80:5000"
redis:
image: redis
Now start the app:
$ docker-compose up -d
Pulling web (username/counter:latest)...
swarm-0: Pulling username/counter:latest... : downloaded
swarm-2: Pulling username/counter:latest... : downloaded
swarm-1: Pulling username/counter:latest... : downloaded
swarm-3: Pulling username/counter:latest... : downloaded
swarm-4: Pulling username/counter:latest... : downloaded
Creating composetest_web_1...
Pulling redis (redis:latest)...
swarm-2: Pulling redis:latest... : downloaded
swarm-1: Pulling redis:latest... : downloaded
swarm-3: Pulling redis:latest... : downloaded
swarm-4: Pulling redis:latest... : downloaded
swarm-0: Pulling redis:latest... : downloaded
Creating composetest_redis_1...
Swarm has created containers for both web and redis, and placed them on different nodes, which you can check with `docker ps`:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
92faad2135c9 redis "/entrypoint.sh redi 43 seconds ago Up 42 seconds swarm-2/composetest_redis_1
adb809e5cdac username/counter "/bin/sh -c 'python 55 seconds ago Up 54 seconds 45.67.8.9:80->5000/tcp swarm-1/composetest_web_1
You can also see that the web container has exposed port 80 on its swarm node. If you curl that IP, youll get a response from the container:
$ curl http://45.67.8.9
Hello World! I have been seen 1 times.
If you hit it repeatedly, the counter will increment, demonstrating that the web and redis container are communicating:
$ curl http://45.67.8.9
Hello World! I have been seen 2 times.
$ curl http://45.67.8.9
Hello World! I have been seen 3 times.
$ curl http://45.67.8.9
Hello World! I have been seen 4 times.

View File

@@ -1,8 +1,8 @@
PyYAML==3.10
docker-py==1.0.0
dockerpty==0.3.2
docker-py==1.3.1
dockerpty==0.3.4
docopt==0.6.1
requests==2.2.1
requests==2.6.1
six==1.7.3
texttable==0.8.2
websocket-client==0.11.0
websocket-client==0.32.0

View File

@@ -1,33 +0,0 @@
#!/bin/bash
if [ -z "$VALIDATE_UPSTREAM" ]; then
# this is kind of an expensive check, so let's not do this twice if we
# are running more than one validate bundlescript
VALIDATE_REPO='https://github.com/docker/fig.git'
VALIDATE_BRANCH='master'
if [ "$TRAVIS" = 'true' -a "$TRAVIS_PULL_REQUEST" != 'false' ]; then
VALIDATE_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git"
VALIDATE_BRANCH="${TRAVIS_BRANCH}"
fi
VALIDATE_HEAD="$(git rev-parse --verify HEAD)"
git fetch -q "$VALIDATE_REPO" "refs/heads/$VALIDATE_BRANCH"
VALIDATE_UPSTREAM="$(git rev-parse --verify FETCH_HEAD)"
VALIDATE_COMMIT_LOG="$VALIDATE_UPSTREAM..$VALIDATE_HEAD"
VALIDATE_COMMIT_DIFF="$VALIDATE_UPSTREAM...$VALIDATE_HEAD"
validate_diff() {
if [ "$VALIDATE_UPSTREAM" != "$VALIDATE_HEAD" ]; then
git diff "$VALIDATE_COMMIT_DIFF" "$@"
fi
}
validate_log() {
if [ "$VALIDATE_UPSTREAM" != "$VALIDATE_HEAD" ]; then
git log "$VALIDATE_COMMIT_LOG" "$@"
fi
}
fi

View File

@@ -7,4 +7,4 @@ chmod 777 `pwd`/dist
pyinstaller -F bin/docker-compose
mv dist/docker-compose dist/docker-compose-Linux-x86_64
dist/docker-compose-Linux-x86_64 --version
dist/docker-compose-Linux-x86_64 version

View File

@@ -1,10 +1,13 @@
#!/bin/bash
set -ex
PATH="/usr/local/bin:$PATH"
rm -rf venv
virtualenv venv
virtualenv -p /usr/local/bin/python venv
venv/bin/pip install -r requirements.txt
venv/bin/pip install -r requirements-dev.txt
venv/bin/pip install .
venv/bin/pyinstaller -F bin/docker-compose
mv dist/docker-compose dist/docker-compose-Darwin-x86_64
dist/docker-compose-Darwin-x86_64 --version
dist/docker-compose-Darwin-x86_64 version

View File

@@ -8,9 +8,6 @@
set -e
>&2 echo "Validating DCO"
script/validate-dco
export DOCKER_VERSIONS=all
. script/test-versions

53
script/prepare-osx Executable file
View File

@@ -0,0 +1,53 @@
#!/bin/bash
set -ex
python_version() {
python -V 2>&1
}
openssl_version() {
python -c "import ssl; print ssl.OPENSSL_VERSION"
}
desired_python_version="2.7.9"
desired_python_brew_version="2.7.9"
python_formula="https://raw.githubusercontent.com/Homebrew/homebrew/1681e193e4d91c9620c4901efd4458d9b6fcda8e/Library/Formula/python.rb"
desired_openssl_version="1.0.1j"
desired_openssl_brew_version="1.0.1j_1"
openssl_formula="https://raw.githubusercontent.com/Homebrew/homebrew/62fc2a1a65e83ba9dbb30b2e0a2b7355831c714b/Library/Formula/openssl.rb"
PATH="/usr/local/bin:$PATH"
if !(which brew); then
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
fi
brew update
if !(python_version | grep "$desired_python_version"); then
if brew list | grep python; then
brew unlink python
fi
brew install "$python_formula"
brew switch python "$desired_python_brew_version"
fi
if !(openssl_version | grep "$desired_openssl_version"); then
if brew list | grep openssl; then
brew unlink openssl
fi
brew install "$openssl_formula"
brew switch openssl "$desired_openssl_brew_version"
fi
echo "*** Using $(python_version)"
echo "*** Using $(openssl_version)"
if !(which virtualenv); then
pip install virtualenv
fi

View File

@@ -9,9 +9,9 @@ docker build -t "$TAG" .
docker run \
--rm \
--volume="/var/run/docker.sock:/var/run/docker.sock" \
--volume="$(pwd):/code" \
-e DOCKER_VERSIONS \
-e "TAG=$TAG" \
-e "affinity:image==$TAG" \
--entrypoint="script/test-versions" \
"$TAG" \
"$@"

View File

@@ -5,10 +5,10 @@
set -e
>&2 echo "Running lint checks"
flake8 compose
flake8 compose tests setup.py
if [ "$DOCKER_VERSIONS" == "" ]; then
DOCKER_VERSIONS="1.5.0"
DOCKER_VERSIONS="default"
elif [ "$DOCKER_VERSIONS" == "all" ]; then
DOCKER_VERSIONS="$ALL_DOCKER_VERSIONS"
fi

View File

@@ -1,58 +0,0 @@
#!/bin/bash
set -e
source "$(dirname "$BASH_SOURCE")/.validate"
adds=$(validate_diff --numstat | awk '{ s += $1 } END { print s }')
dels=$(validate_diff --numstat | awk '{ s += $2 } END { print s }')
notDocs="$(validate_diff --numstat | awk '$3 !~ /^docs\// { print $3 }')"
: ${adds:=0}
: ${dels:=0}
# "Username may only contain alphanumeric characters or dashes and cannot begin with a dash"
githubUsernameRegex='[a-zA-Z0-9][a-zA-Z0-9-]+'
# https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work
dcoPrefix='Signed-off-by:'
dcoRegex="^(Docker-DCO-1.1-)?$dcoPrefix ([^<]+) <([^<>@]+@[^<>]+)>( \\(github: ($githubUsernameRegex)\\))?$"
check_dco() {
grep -qE "$dcoRegex"
}
if [ $adds -eq 0 -a $dels -eq 0 ]; then
echo '0 adds, 0 deletions; nothing to validate! :)'
elif [ -z "$notDocs" -a $adds -le 1 -a $dels -le 1 ]; then
echo 'Congratulations! DCO small-patch-exception material!'
else
commits=( $(validate_log --format='format:%H%n') )
badCommits=()
for commit in "${commits[@]}"; do
if [ -z "$(git log -1 --format='format:' --name-status "$commit")" ]; then
# no content (ie, Merge commit, etc)
continue
fi
if ! git log -1 --format='format:%B' "$commit" | check_dco; then
badCommits+=( "$commit" )
fi
done
if [ ${#badCommits[@]} -eq 0 ]; then
echo "Congratulations! All commits are properly signed with the DCO!"
else
{
echo "These commits do not have a proper '$dcoPrefix' marker:"
for commit in "${badCommits[@]}"; do
echo " - $commit"
done
echo
echo 'Please amend each commit to include a properly formatted DCO marker.'
echo
echo 'Visit the following URL for information about the Docker DCO:'
echo ' https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work'
echo
} >&2
false
fi
fi

View File

@@ -1,18 +1,23 @@
#!/bin/bash
if [ "$DOCKER_VERSION" == "" ]; then
DOCKER_VERSION="1.5.0"
if [ "$DOCKER_VERSION" != "" ] && [ "$DOCKER_VERSION" != "default" ]; then
ln -fs "/usr/local/bin/docker-$DOCKER_VERSION" "/usr/local/bin/docker"
fi
ln -fs "/usr/local/bin/docker-$DOCKER_VERSION" "/usr/local/bin/docker"
# If a pidfile is still around (for example after a container restart),
# delete it so that docker can start.
rm -rf /var/run/docker.pid
docker -d $DOCKER_DAEMON_ARGS &>/var/log/docker.log &
docker -d --storage-driver="overlay" &>/var/log/docker.log &
docker_pid=$!
>&2 echo "Waiting for Docker to start..."
while ! docker ps &>/dev/null; do
if ! kill -0 "$docker_pid" &>/dev/null; then
>&2 echo "Docker failed to start"
cat /var/log/docker.log
exit 1
fi
sleep 1
done

View File

@@ -27,14 +27,15 @@ def find_version(*file_paths):
install_requires = [
'docopt >= 0.6.1, < 0.7',
'PyYAML >= 3.10, < 4',
'requests >= 2.2.1, < 2.6',
'requests >= 2.6.1, < 2.7',
'texttable >= 0.8.1, < 0.9',
'websocket-client >= 0.11.0, < 1.0',
'docker-py >= 1.0.0, < 1.2',
'dockerpty >= 0.3.2, < 0.4',
'websocket-client >= 0.32.0, < 1.0',
'docker-py >= 1.3.1, < 1.4',
'dockerpty >= 0.3.4, < 0.4',
'six >= 1.3.0, < 2',
]
tests_require = [
'mock >= 1.0.1',
'nose',
@@ -54,7 +55,7 @@ setup(
url='https://www.docker.com/',
author='Docker, Inc.',
license='Apache License 2.0',
packages=find_packages(exclude=[ 'tests.*', 'tests' ]),
packages=find_packages(exclude=['tests.*', 'tests']),
include_package_data=True,
test_suite='nose.collector',
install_requires=install_requires,

View File

@@ -1,7 +1,8 @@
import sys
if sys.version_info >= (2,7):
import unittest
else:
import unittest2 as unittest
import mock # noqa
if sys.version_info >= (2, 7):
import unittest # NOQA
else:
import unittest2 as unittest # NOQA

View File

@@ -1,6 +1,6 @@
simple:
image: busybox:latest
command: /bin/sleep 300
command: top
another:
image: busybox:latest
command: /bin/sleep 300
command: top

View File

@@ -1,2 +1,3 @@
FROM busybox:latest
LABEL com.docker.compose.test_image=true
CMD echo "success"

View File

@@ -1,3 +1,4 @@
FROM busybox
FROM busybox:latest
LABEL com.docker.compose.test_image=true
VOLUME /data
CMD sleep 3000
CMD top

View File

@@ -1,2 +1,3 @@
FROM busybox:latest
LABEL com.docker.compose.test_image=true
ENTRYPOINT echo "From prebuilt entrypoint"

View File

@@ -1,6 +1,6 @@
service:
image: busybox:latest
command: sleep 5
command: top
environment:
foo: bar

View File

@@ -2,7 +2,7 @@ myweb:
extends:
file: common.yml
service: web
command: sleep 300
command: top
links:
- "mydb:db"
environment:
@@ -13,4 +13,4 @@ myweb:
BAZ: "2"
mydb:
image: busybox
command: sleep 300
command: top

View File

@@ -0,0 +1,9 @@
myweb:
extends:
service: web
environment:
- "BAR=1"
web:
image: busybox
environment:
- "BAZ=3"

View File

@@ -0,0 +1,6 @@
dnebase:
build: nonexistent.path
command: /bin/true
environment:
- FOO=1
- BAR=1

View File

@@ -0,0 +1,8 @@
dnechild:
extends:
file: nonexistent-path-base.yml
service: dnebase
image: busybox
command: /bin/true
environment:
- BAR=2

View File

@@ -0,0 +1,16 @@
myweb:
extends:
file: specify-file-as-self.yml
service: web
environment:
- "BAR=1"
web:
extends:
file: specify-file-as-self.yml
service: otherweb
image: busybox
environment:
- "BAZ=3"
otherweb:
environment:
- "YEP=1"

View File

@@ -1,11 +1,11 @@
db:
image: busybox:latest
command: /bin/sleep 300
command: top
web:
image: busybox:latest
command: /bin/sleep 300
command: top
links:
- db:db
console:
image: busybox:latest
command: /bin/sleep 300
command: top

View File

@@ -1,3 +1,3 @@
definedinyamlnotyml:
image: busybox:latest
command: /bin/sleep 300
command: top

View File

@@ -1,3 +1,3 @@
yetanother:
image: busybox:latest
command: /bin/sleep 300
command: top

View File

@@ -1,6 +1,6 @@
simple:
image: busybox:latest
command: /bin/sleep 300
command: top
another:
image: busybox:latest
command: /bin/sleep 300
command: top

View File

@@ -0,0 +1,6 @@
simple:
image: busybox:latest
command: /bin/sleep 300
ports:
- '3000'

View File

@@ -1,7 +1,7 @@
simple:
image: busybox:latest
command: /bin/sleep 300
command: top
ports:
- '3000'
- '49152:3001'

View File

@@ -1,6 +1,6 @@
simple:
image: busybox:latest
command: /bin/sleep 300
command: top
another:
image: busybox:latest
command: /bin/sleep 300
command: top

View File

@@ -1,2 +1,3 @@
FROM busybox:latest
LABEL com.docker.compose.test_image=true
CMD echo "success"

View File

@@ -1,12 +1,16 @@
from __future__ import absolute_import
from operator import attrgetter
import sys
import os
import shlex
from six import StringIO
from mock import patch
from .testcases import DockerClientTestCase
from compose.cli.main import TopLevelCommand
from compose.cli.errors import UserError
from compose.project import NoSuchService
class CLITestCase(DockerClientTestCase):
@@ -21,6 +25,9 @@ class CLITestCase(DockerClientTestCase):
sys.exit = self.old_sys_exit
self.project.kill()
self.project.remove_stopped()
for container in self.project.containers(stopped=True, one_off=True):
container.remove(force=True)
super(CLITestCase, self).tearDown()
@property
def project(self):
@@ -30,7 +37,7 @@ class CLITestCase(DockerClientTestCase):
if hasattr(self, '_project'):
return self._project
return self.command.get_project(self.command.get_config_path())
return self.command.get_project()
def test_help(self):
old_base_dir = self.command.base_dir
@@ -62,6 +69,10 @@ class CLITestCase(DockerClientTestCase):
@patch('sys.stdout', new_callable=StringIO)
def test_ps_alternate_composefile(self, mock_stdout):
config_path = os.path.abspath(
'tests/fixtures/multiple-composefiles/compose2.yml')
self._project = self.command.get_project(config_path)
self.command.base_dir = 'tests/fixtures/multiple-composefiles'
self.command.dispatch(['-f', 'compose2.yml', 'up', '-d'], None)
self.command.dispatch(['-f', 'compose2.yml', 'ps'], None)
@@ -126,21 +137,21 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(len(db.containers()), 0)
self.assertEqual(len(console.containers()), 0)
def test_up_with_recreate(self):
def test_up_with_force_recreate(self):
self.command.dispatch(['up', '-d'], None)
service = self.project.get_service('simple')
self.assertEqual(len(service.containers()), 1)
old_ids = [c.id for c in service.containers()]
self.command.dispatch(['up', '-d'], None)
self.command.dispatch(['up', '-d', '--force-recreate'], None)
self.assertEqual(len(service.containers()), 1)
new_ids = [c.id for c in service.containers()]
self.assertNotEqual(old_ids, new_ids)
def test_up_with_keep_old(self):
def test_up_with_no_recreate(self):
self.command.dispatch(['up', '-d'], None)
service = self.project.get_service('simple')
self.assertEqual(len(service.containers()), 1)
@@ -154,6 +165,23 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(old_ids, new_ids)
def test_up_with_force_recreate_and_no_recreate(self):
with self.assertRaises(UserError):
self.command.dispatch(['up', '-d', '--force-recreate', '--no-recreate'], None)
def test_up_with_timeout(self):
self.command.dispatch(['up', '-d', '-t', '1'], None)
service = self.project.get_service('simple')
another = self.project.get_service('another')
self.assertEqual(len(service.containers()), 1)
self.assertEqual(len(another.containers()), 1)
# Ensure containers don't have stdin and stdout connected in -d mode
config = service.containers()[0].inspect()['Config']
self.assertFalse(config['AttachStderr'])
self.assertFalse(config['AttachStdout'])
self.assertFalse(config['AttachStdin'])
@patch('dockerpty.start')
def test_run_service_without_links(self, mock_stdout):
self.command.base_dir = 'tests/fixtures/links-composefile'
@@ -200,13 +228,10 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(old_ids, new_ids)
@patch('dockerpty.start')
def test_run_without_command(self, __):
def test_run_without_command(self, _):
self.command.base_dir = 'tests/fixtures/commands-composefile'
self.check_build('tests/fixtures/simple-dockerfile', tag='composetest_test')
for c in self.project.containers(stopped=True, one_off=True):
c.remove()
self.command.dispatch(['run', 'implicit'], None)
service = self.project.get_service('implicit')
containers = service.containers(stopped=True, one_off=True)
@@ -234,8 +259,8 @@ class CLITestCase(DockerClientTestCase):
service = self.project.get_service(name)
container = service.containers(stopped=True, one_off=True)[0]
self.assertEqual(
container.human_readable_command,
u'/bin/echo helloworld'
shlex.split(container.human_readable_command),
[u'/bin/echo', u'helloworld'],
)
@patch('dockerpty.start')
@@ -332,6 +357,21 @@ class CLITestCase(DockerClientTestCase):
self.command.dispatch(['rm', '-f'], None)
self.assertEqual(len(service.containers(stopped=True)), 0)
def test_stop(self):
self.command.dispatch(['up', '-d'], None)
service = self.project.get_service('simple')
self.assertEqual(len(service.containers()), 1)
self.assertTrue(service.containers()[0].is_running)
self.command.dispatch(['stop', '-t', '1'], None)
self.assertEqual(len(service.containers(stopped=True)), 1)
self.assertFalse(service.containers(stopped=True)[0].is_running)
def test_logs_invalid_service_name(self):
with self.assertRaises(NoSuchService):
self.command.dispatch(['logs', 'madeupname'], None)
def test_kill(self):
self.command.dispatch(['up', '-d'], None)
service = self.project.get_service('simple')
@@ -343,22 +383,22 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(len(service.containers(stopped=True)), 1)
self.assertFalse(service.containers(stopped=True)[0].is_running)
def test_kill_signal_sigint(self):
def test_kill_signal_sigstop(self):
self.command.dispatch(['up', '-d'], None)
service = self.project.get_service('simple')
self.assertEqual(len(service.containers()), 1)
self.assertTrue(service.containers()[0].is_running)
self.command.dispatch(['kill', '-s', 'SIGINT'], None)
self.command.dispatch(['kill', '-s', 'SIGSTOP'], None)
self.assertEqual(len(service.containers()), 1)
# The container is still running. It has been only interrupted
# The container is still running. It has only been paused
self.assertTrue(service.containers()[0].is_running)
def test_kill_interrupted_service(self):
def test_kill_stopped_service(self):
self.command.dispatch(['up', '-d'], None)
service = self.project.get_service('simple')
self.command.dispatch(['kill', '-s', 'SIGINT'], None)
self.command.dispatch(['kill', '-s', 'SIGSTOP'], None)
self.assertTrue(service.containers()[0].is_running)
self.command.dispatch(['kill', '-s', 'SIGKILL'], None)
@@ -371,7 +411,7 @@ class CLITestCase(DockerClientTestCase):
container = service.create_container()
service.start_container(container)
started_at = container.dictionary['State']['StartedAt']
self.command.dispatch(['restart'], None)
self.command.dispatch(['restart', '-t', '1'], None)
container.inspect()
self.assertNotEqual(
container.dictionary['State']['FinishedAt'],
@@ -405,7 +445,6 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(len(project.get_service('another').containers()), 0)
def test_port(self):
self.command.base_dir = 'tests/fixtures/ports-composefile'
self.command.dispatch(['up', '-d'], None)
container = self.project.get_service('simple').get_container()
@@ -419,6 +458,27 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(get_port(3001), "0.0.0.0:49152")
self.assertEqual(get_port(3002), "")
def test_port_with_scale(self):
self.command.base_dir = 'tests/fixtures/ports-composefile-scale'
self.command.dispatch(['scale', 'simple=2'], None)
containers = sorted(
self.project.containers(service_names=['simple']),
key=attrgetter('name'))
@patch('sys.stdout', new_callable=StringIO)
def get_port(number, mock_stdout, index=None):
if index is None:
self.command.dispatch(['port', 'simple', str(number)], None)
else:
self.command.dispatch(['port', '--index=' + str(index), 'simple', str(number)], None)
return mock_stdout.getvalue().rstrip()
self.assertEqual(get_port(3000), containers[0].get_local_port(3000))
self.assertEqual(get_port(3000, index=1), containers[0].get_local_port(3000))
self.assertEqual(get_port(3000, index=2), containers[1].get_local_port(3000))
self.assertEqual(get_port(3002), "")
def test_env_file_relative_to_compose_file(self):
config_path = os.path.abspath('tests/fixtures/env-file/docker-compose.yml')
self.command.dispatch(['-f', config_path, 'up', '-d'], None)

View File

@@ -0,0 +1,218 @@
import unittest
from mock import Mock
from docker.errors import APIError
from compose import legacy
from compose.project import Project
from .testcases import DockerClientTestCase
class UtilitiesTestCase(unittest.TestCase):
def test_has_container(self):
self.assertTrue(
legacy.has_container("composetest", "web", "composetest_web_1", one_off=False),
)
self.assertFalse(
legacy.has_container("composetest", "web", "composetest_web_run_1", one_off=False),
)
def test_has_container_one_off(self):
self.assertFalse(
legacy.has_container("composetest", "web", "composetest_web_1", one_off=True),
)
self.assertTrue(
legacy.has_container("composetest", "web", "composetest_web_run_1", one_off=True),
)
def test_has_container_different_project(self):
self.assertFalse(
legacy.has_container("composetest", "web", "otherapp_web_1", one_off=False),
)
self.assertFalse(
legacy.has_container("composetest", "web", "otherapp_web_run_1", one_off=True),
)
def test_has_container_different_service(self):
self.assertFalse(
legacy.has_container("composetest", "web", "composetest_db_1", one_off=False),
)
self.assertFalse(
legacy.has_container("composetest", "web", "composetest_db_run_1", one_off=True),
)
def test_is_valid_name(self):
self.assertTrue(
legacy.is_valid_name("composetest_web_1", one_off=False),
)
self.assertFalse(
legacy.is_valid_name("composetest_web_run_1", one_off=False),
)
def test_is_valid_name_one_off(self):
self.assertFalse(
legacy.is_valid_name("composetest_web_1", one_off=True),
)
self.assertTrue(
legacy.is_valid_name("composetest_web_run_1", one_off=True),
)
def test_is_valid_name_invalid(self):
self.assertFalse(
legacy.is_valid_name("foo"),
)
self.assertFalse(
legacy.is_valid_name("composetest_web_lol_1", one_off=True),
)
def test_get_legacy_containers(self):
client = Mock()
client.containers.return_value = [
{
"Id": "abc123",
"Image": "def456",
"Name": "composetest_web_1",
"Labels": None,
},
{
"Id": "ghi789",
"Image": "def456",
"Name": None,
"Labels": None,
},
{
"Id": "jkl012",
"Image": "def456",
"Labels": None,
},
]
containers = legacy.get_legacy_containers(client, "composetest", ["web"])
self.assertEqual(len(containers), 1)
self.assertEqual(containers[0].id, 'abc123')
class LegacyTestCase(DockerClientTestCase):
def setUp(self):
super(LegacyTestCase, self).setUp()
self.containers = []
db = self.create_service('db')
web = self.create_service('web', links=[(db, 'db')])
nginx = self.create_service('nginx', links=[(web, 'web')])
self.services = [db, web, nginx]
self.project = Project('composetest', self.services, self.client)
# Create a legacy container for each service
for service in self.services:
service.ensure_image_exists()
container = self.client.create_container(
name='{}_{}_1'.format(self.project.name, service.name),
**service.options
)
self.client.start(container)
self.containers.append(container)
# Create a single one-off legacy container
self.containers.append(self.client.create_container(
name='{}_{}_run_1'.format(self.project.name, db.name),
**self.services[0].options
))
def tearDown(self):
super(LegacyTestCase, self).tearDown()
for container in self.containers:
try:
self.client.kill(container)
except APIError:
pass
try:
self.client.remove_container(container)
except APIError:
pass
def get_legacy_containers(self, **kwargs):
return legacy.get_legacy_containers(
self.client,
self.project.name,
[s.name for s in self.services],
**kwargs
)
def test_get_legacy_container_names(self):
self.assertEqual(len(self.get_legacy_containers()), len(self.services))
def test_get_legacy_container_names_one_off(self):
self.assertEqual(len(self.get_legacy_containers(one_off=True)), 1)
def test_migration_to_labels(self):
# Trying to get the container list raises an exception
with self.assertRaises(legacy.LegacyContainersError) as cm:
self.project.containers(stopped=True)
self.assertEqual(
set(cm.exception.names),
set(['composetest_db_1', 'composetest_web_1', 'composetest_nginx_1']),
)
self.assertEqual(
set(cm.exception.one_off_names),
set(['composetest_db_run_1']),
)
# Migrate the containers
legacy.migrate_project_to_labels(self.project)
# Getting the list no longer raises an exception
containers = self.project.containers(stopped=True)
self.assertEqual(len(containers), len(self.services))
def test_migration_one_off(self):
# We've already migrated
legacy.migrate_project_to_labels(self.project)
# Trying to create a one-off container results in a Docker API error
with self.assertRaises(APIError) as cm:
self.project.get_service('db').create_container(one_off=True)
# Checking for legacy one-off containers raises an exception
with self.assertRaises(legacy.LegacyOneOffContainersError) as cm:
legacy.check_for_legacy_containers(
self.client,
self.project.name,
['db'],
allow_one_off=False,
)
self.assertEqual(
set(cm.exception.one_off_names),
set(['composetest_db_run_1']),
)
# Remove the old one-off container
c = self.client.inspect_container('composetest_db_run_1')
self.client.remove_container(c)
# Checking no longer raises an exception
legacy.check_for_legacy_containers(
self.client,
self.project.name,
['db'],
allow_one_off=False,
)
# Creating a one-off container no longer results in an API error
self.project.get_service('db').create_container(one_off=True)
self.assertIsInstance(self.client.inspect_container('composetest_db_run_1'), dict)

View File

@@ -1,13 +1,57 @@
from __future__ import unicode_literals
from compose import config
from compose.const import LABEL_PROJECT
from compose.project import Project
from compose.container import Container
from .testcases import DockerClientTestCase
def build_service_dicts(service_config):
return config.load(config.ConfigDetails(service_config, 'working_dir', None))
class ProjectTest(DockerClientTestCase):
def test_containers(self):
web = self.create_service('web')
db = self.create_service('db')
project = Project('composetest', [web, db], self.client)
project.up()
containers = project.containers()
self.assertEqual(len(containers), 2)
def test_containers_with_service_names(self):
web = self.create_service('web')
db = self.create_service('db')
project = Project('composetest', [web, db], self.client)
project.up()
containers = project.containers(['web'])
self.assertEqual(
[c.name for c in containers],
['composetest_web_1'])
def test_containers_with_extra_service(self):
web = self.create_service('web')
web_1 = web.create_container()
db = self.create_service('db')
db_1 = db.create_container()
self.create_service('extra').create_container()
project = Project('composetest', [web, db], self.client)
self.assertEqual(
set(project.containers(stopped=True)),
set([web_1, db_1]),
)
def test_volumes_from_service(self):
service_dicts = config.from_dictionary({
service_dicts = build_service_dicts({
'data': {
'image': 'busybox:latest',
'volumes': ['/var/data'],
@@ -16,7 +60,7 @@ class ProjectTest(DockerClientTestCase):
'image': 'busybox:latest',
'volumes_from': ['data'],
},
}, working_dir='.')
})
project = Project.from_dicts(
name='composetest',
service_dicts=service_dicts,
@@ -32,10 +76,11 @@ class ProjectTest(DockerClientTestCase):
image='busybox:latest',
volumes=['/var/data'],
name='composetest_data_container',
labels={LABEL_PROJECT: 'composetest'},
)
project = Project.from_dicts(
name='composetest',
service_dicts=config.from_dictionary({
service_dicts=build_service_dicts({
'db': {
'image': 'busybox:latest',
'volumes_from': ['composetest_data_container'],
@@ -46,21 +91,18 @@ class ProjectTest(DockerClientTestCase):
db = project.get_service('db')
self.assertEqual(db.volumes_from, [data_container])
project.kill()
project.remove_stopped()
def test_net_from_service(self):
project = Project.from_dicts(
name='composetest',
service_dicts=config.from_dictionary({
service_dicts=build_service_dicts({
'net': {
'image': 'busybox:latest',
'command': ["/bin/sleep", "300"]
'command': ["top"]
},
'web': {
'image': 'busybox:latest',
'net': 'container:net',
'command': ["/bin/sleep", "300"]
'command': ["top"]
},
}),
client=self.client,
@@ -70,23 +112,21 @@ class ProjectTest(DockerClientTestCase):
web = project.get_service('web')
net = project.get_service('net')
self.assertEqual(web._get_net(), 'container:'+net.containers()[0].id)
project.kill()
project.remove_stopped()
self.assertEqual(web.net.mode, 'container:' + net.containers()[0].id)
def test_net_from_container(self):
net_container = Container.create(
self.client,
image='busybox:latest',
name='composetest_net_container',
command='/bin/sleep 300'
command='top',
labels={LABEL_PROJECT: 'composetest'},
)
net_container.start()
project = Project.from_dicts(
name='composetest',
service_dicts=config.from_dictionary({
service_dicts=build_service_dicts({
'web': {
'image': 'busybox:latest',
'net': 'container:composetest_net_container'
@@ -98,10 +138,7 @@ class ProjectTest(DockerClientTestCase):
project.up()
web = project.get_service('web')
self.assertEqual(web._get_net(), 'container:'+net_container.id)
project.kill()
project.remove_stopped()
self.assertEqual(web.net.mode, 'container:' + net_container.id)
def test_start_stop_kill_remove(self):
web = self.create_service('web')
@@ -148,10 +185,19 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(len(db.containers()), 1)
self.assertEqual(len(web.containers()), 0)
project.kill()
project.remove_stopped()
def test_project_up_starts_uncreated_services(self):
db = self.create_service('db')
web = self.create_service('web', links=[(db, 'db')])
project = Project('composetest', [db, web], self.client)
project.up(['db'])
self.assertEqual(len(project.containers()), 1)
def test_project_up_recreates_containers(self):
project.up()
self.assertEqual(len(project.containers()), 2)
self.assertEqual(len(db.containers()), 1)
self.assertEqual(len(web.containers()), 1)
def test_recreate_preserves_volumes(self):
web = self.create_service('web')
db = self.create_service('db', volumes=['/etc'])
project = Project('composetest', [web, db], self.client)
@@ -163,16 +209,13 @@ class ProjectTest(DockerClientTestCase):
old_db_id = project.containers()[0].id
db_volume_path = project.containers()[0].get('Volumes./etc')
project.up()
project.up(force_recreate=True)
self.assertEqual(len(project.containers()), 2)
db_container = [c for c in project.containers() if 'db' in c.name][0]
self.assertNotEqual(db_container.id, old_db_id)
self.assertEqual(db_container.get('Volumes./etc'), db_volume_path)
project.kill()
project.remove_stopped()
def test_project_up_with_no_recreate_running(self):
web = self.create_service('web')
db = self.create_service('db', volumes=['/var/db'])
@@ -185,7 +228,7 @@ class ProjectTest(DockerClientTestCase):
old_db_id = project.containers()[0].id
db_volume_path = project.containers()[0].inspect()['Volumes']['/var/db']
project.up(recreate=False)
project.up(allow_recreate=False)
self.assertEqual(len(project.containers()), 2)
db_container = [c for c in project.containers() if 'db' in c.name][0]
@@ -193,9 +236,6 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(db_container.inspect()['Volumes']['/var/db'],
db_volume_path)
project.kill()
project.remove_stopped()
def test_project_up_with_no_recreate_stopped(self):
web = self.create_service('web')
db = self.create_service('db', volumes=['/var/db'])
@@ -204,7 +244,7 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(len(project.containers()), 0)
project.up(['db'])
project.stop()
project.kill()
old_containers = project.containers(stopped=True)
@@ -212,19 +252,17 @@ class ProjectTest(DockerClientTestCase):
old_db_id = old_containers[0].id
db_volume_path = old_containers[0].inspect()['Volumes']['/var/db']
project.up(recreate=False)
project.up(allow_recreate=False)
new_containers = project.containers(stopped=True)
self.assertEqual(len(new_containers), 2)
self.assertEqual([c.is_running for c in new_containers], [True, True])
db_container = [c for c in new_containers if 'db' in c.name][0]
self.assertEqual(db_container.id, old_db_id)
self.assertEqual(db_container.inspect()['Volumes']['/var/db'],
db_volume_path)
project.kill()
project.remove_stopped()
def test_project_up_without_all_services(self):
console = self.create_service('console')
db = self.create_service('db')
@@ -237,9 +275,6 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(len(db.containers()), 1)
self.assertEqual(len(console.containers()), 1)
project.kill()
project.remove_stopped()
def test_project_up_starts_links(self):
console = self.create_service('console')
db = self.create_service('db', volumes=['/var/db'])
@@ -255,29 +290,26 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(len(db.containers()), 1)
self.assertEqual(len(console.containers()), 0)
project.kill()
project.remove_stopped()
def test_project_up_starts_depends(self):
project = Project.from_dicts(
name='composetest',
service_dicts=config.from_dictionary({
service_dicts=build_service_dicts({
'console': {
'image': 'busybox:latest',
'command': ["/bin/sleep", "300"],
'command': ["top"],
},
'data' : {
'data': {
'image': 'busybox:latest',
'command': ["/bin/sleep", "300"]
'command': ["top"]
},
'db': {
'image': 'busybox:latest',
'command': ["/bin/sleep", "300"],
'command': ["top"],
'volumes_from': ['data'],
},
'web': {
'image': 'busybox:latest',
'command': ["/bin/sleep", "300"],
'command': ["top"],
'links': ['db'],
},
}),
@@ -293,29 +325,26 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(len(project.get_service('data').containers()), 1)
self.assertEqual(len(project.get_service('console').containers()), 0)
project.kill()
project.remove_stopped()
def test_project_up_with_no_deps(self):
project = Project.from_dicts(
name='composetest',
service_dicts=config.from_dictionary({
service_dicts=build_service_dicts({
'console': {
'image': 'busybox:latest',
'command': ["/bin/sleep", "300"],
'command': ["top"],
},
'data' : {
'data': {
'image': 'busybox:latest',
'command': ["/bin/sleep", "300"]
'command': ["top"]
},
'db': {
'image': 'busybox:latest',
'command': ["/bin/sleep", "300"],
'command': ["top"],
'volumes_from': ['data'],
},
'web': {
'image': 'busybox:latest',
'command': ["/bin/sleep", "300"],
'command': ["top"],
'links': ['db'],
},
}),
@@ -332,9 +361,6 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(len(project.get_service('data').containers(stopped=True)), 1)
self.assertEqual(len(project.get_service('console').containers()), 0)
project.kill()
project.remove_stopped()
def test_unscale_after_restart(self):
web = self.create_service('web')
project = Project('composetest', [web], self.client)
@@ -359,5 +385,3 @@ class ProjectTest(DockerClientTestCase):
project.up()
service = project.get_service('web')
self.assertEqual(len(service.containers()), 1)
project.kill()
project.remove_stopped()

View File

@@ -0,0 +1,48 @@
from __future__ import unicode_literals
from __future__ import absolute_import
import mock
from compose.project import Project
from .testcases import DockerClientTestCase
class ResilienceTest(DockerClientTestCase):
def setUp(self):
self.db = self.create_service('db', volumes=['/var/db'], command='top')
self.project = Project('composetest', [self.db], self.client)
container = self.db.create_container()
self.db.start_container(container)
self.host_path = container.get('Volumes')['/var/db']
def test_successful_recreate(self):
self.project.up(force_recreate=True)
container = self.db.containers()[0]
self.assertEqual(container.get('Volumes')['/var/db'], self.host_path)
def test_create_failure(self):
with mock.patch('compose.service.Service.create_container', crash):
with self.assertRaises(Crash):
self.project.up(force_recreate=True)
self.project.up()
container = self.db.containers()[0]
self.assertEqual(container.get('Volumes')['/var/db'], self.host_path)
def test_start_failure(self):
with mock.patch('compose.service.Service.start_container', crash):
with self.assertRaises(Crash):
self.project.up(force_recreate=True)
self.project.up()
container = self.db.containers()[0]
self.assertEqual(container.get('Volumes')['/var/db'], self.host_path)
class Crash(Exception):
pass
def crash(*args, **kwargs):
raise Crash()

View File

@@ -2,13 +2,28 @@ from __future__ import unicode_literals
from __future__ import absolute_import
import os
from os import path
import mock
from compose import Service
from compose.service import CannotBeScaledError
from compose.container import Container
from docker.errors import APIError
from mock import patch
import tempfile
import shutil
from six import StringIO, text_type
from .testcases import DockerClientTestCase
from compose import __version__
from compose.const import (
LABEL_CONTAINER_NUMBER,
LABEL_ONE_OFF,
LABEL_PROJECT,
LABEL_SERVICE,
LABEL_VERSION,
)
from compose.container import Container
from compose.service import build_extra_hosts
from compose.service import ConfigError
from compose.service import ConvergencePlan
from compose.service import Net
from compose.service import Service
def create_and_start_container(service, **override_options):
@@ -99,13 +114,101 @@ class ServiceTest(DockerClientTestCase):
service = self.create_service('db', volumes=['/var/db'])
container = service.create_container()
service.start_container(container)
self.assertIn('/var/db', container.inspect()['Volumes'])
self.assertIn('/var/db', container.get('Volumes'))
def test_create_container_with_volume_driver(self):
service = self.create_service('db', volume_driver='foodriver')
container = service.create_container()
service.start_container(container)
self.assertEqual('foodriver', container.get('Config.VolumeDriver'))
def test_create_container_with_cpu_shares(self):
service = self.create_service('db', cpu_shares=73)
container = service.create_container()
service.start_container(container)
self.assertEqual(container.inspect()['Config']['CpuShares'], 73)
self.assertEqual(container.get('HostConfig.CpuShares'), 73)
def test_build_extra_hosts(self):
# string
self.assertRaises(ConfigError, lambda: build_extra_hosts("www.example.com: 192.168.0.17"))
# list of strings
self.assertEqual(build_extra_hosts(
["www.example.com:192.168.0.17"]),
{'www.example.com': '192.168.0.17'})
self.assertEqual(build_extra_hosts(
["www.example.com: 192.168.0.17"]),
{'www.example.com': '192.168.0.17'})
self.assertEqual(build_extra_hosts(
["www.example.com: 192.168.0.17",
"static.example.com:192.168.0.19",
"api.example.com: 192.168.0.18"]),
{'www.example.com': '192.168.0.17',
'static.example.com': '192.168.0.19',
'api.example.com': '192.168.0.18'})
# list of dictionaries
self.assertRaises(ConfigError, lambda: build_extra_hosts(
[{'www.example.com': '192.168.0.17'},
{'api.example.com': '192.168.0.18'}]))
# dictionaries
self.assertEqual(build_extra_hosts(
{'www.example.com': '192.168.0.17',
'api.example.com': '192.168.0.18'}),
{'www.example.com': '192.168.0.17',
'api.example.com': '192.168.0.18'})
def test_create_container_with_extra_hosts_list(self):
extra_hosts = ['somehost:162.242.195.82', 'otherhost:50.31.209.229']
service = self.create_service('db', extra_hosts=extra_hosts)
container = service.create_container()
service.start_container(container)
self.assertEqual(set(container.get('HostConfig.ExtraHosts')), set(extra_hosts))
def test_create_container_with_extra_hosts_string(self):
extra_hosts = 'somehost:162.242.195.82'
service = self.create_service('db', extra_hosts=extra_hosts)
self.assertRaises(ConfigError, lambda: service.create_container())
def test_create_container_with_extra_hosts_list_of_dicts(self):
extra_hosts = [{'somehost': '162.242.195.82'}, {'otherhost': '50.31.209.229'}]
service = self.create_service('db', extra_hosts=extra_hosts)
self.assertRaises(ConfigError, lambda: service.create_container())
def test_create_container_with_extra_hosts_dicts(self):
extra_hosts = {'somehost': '162.242.195.82', 'otherhost': '50.31.209.229'}
extra_hosts_list = ['somehost:162.242.195.82', 'otherhost:50.31.209.229']
service = self.create_service('db', extra_hosts=extra_hosts)
container = service.create_container()
service.start_container(container)
self.assertEqual(set(container.get('HostConfig.ExtraHosts')), set(extra_hosts_list))
def test_create_container_with_cpu_set(self):
service = self.create_service('db', cpuset='0')
container = service.create_container()
service.start_container(container)
self.assertEqual(container.get('HostConfig.CpusetCpus'), '0')
def test_create_container_with_read_only_root_fs(self):
read_only = True
service = self.create_service('db', read_only=read_only)
container = service.create_container()
service.start_container(container)
self.assertEqual(container.get('HostConfig.ReadonlyRootfs'), read_only, container.get('HostConfig'))
def test_create_container_with_security_opt(self):
security_opt = ['label:disable']
service = self.create_service('db', security_opt=security_opt)
container = service.create_container()
service.start_container(container)
self.assertEqual(set(container.get('HostConfig.SecurityOpt')), set(security_opt))
def test_create_container_with_mac_address(self):
service = self.create_service('db', mac_address='02:42:ac:11:65:43')
container = service.create_container()
service.start_container(container)
self.assertEqual(container.inspect()['Config']['MacAddress'], '02:42:ac:11:65:43')
def test_create_container_with_specified_volume(self):
host_path = '/tmp/host-path'
@@ -121,9 +224,55 @@ class ServiceTest(DockerClientTestCase):
# Match the last component ("host-path"), because boot2docker symlinks /tmp
actual_host_path = volumes[container_path]
self.assertTrue(path.basename(actual_host_path) == path.basename(host_path),
msg=("Last component differs: %s, %s" % (actual_host_path, host_path)))
msg=("Last component differs: %s, %s" % (actual_host_path, host_path)))
@mock.patch.dict(os.environ)
def test_recreate_preserves_volume_with_trailing_slash(self):
"""
When the Compose file specifies a trailing slash in the container path, make
sure we copy the volume over when recreating.
"""
service = self.create_service('data', volumes=['/data/'])
old_container = create_and_start_container(service)
volume_path = old_container.get('Volumes')['/data']
new_container = service.recreate_container(old_container)
self.assertEqual(new_container.get('Volumes')['/data'], volume_path)
def test_duplicate_volume_trailing_slash(self):
"""
When an image specifies a volume, and the Compose file specifies a host path
but adds a trailing slash, make sure that we don't create duplicate binds.
"""
host_path = '/tmp/data'
container_path = '/data'
volumes = ['{}:{}/'.format(host_path, container_path)]
tmp_container = self.client.create_container(
'busybox', 'true',
volumes={container_path: {}},
labels={'com.docker.compose.test_image': 'true'},
)
image = self.client.commit(tmp_container)['Id']
service = self.create_service('db', image=image, volumes=volumes)
old_container = create_and_start_container(service)
self.assertEqual(
old_container.get('Config.Volumes'),
{container_path: {}},
)
service = self.create_service('db', image=image, volumes=volumes)
new_container = service.recreate_container(old_container)
self.assertEqual(
new_container.get('Config.Volumes'),
{container_path: {}},
)
self.assertEqual(service.containers(stopped=False), [new_container])
@patch.dict(os.environ)
def test_create_container_with_home_and_env_var_in_volume_path(self):
os.environ['VOLUME_NAME'] = 'my-volume'
os.environ['HOME'] = '/tmp/home-dir'
@@ -144,7 +293,12 @@ class ServiceTest(DockerClientTestCase):
def test_create_container_with_volumes_from(self):
volume_service = self.create_service('data')
volume_container_1 = volume_service.create_container()
volume_container_2 = Container.create(self.client, image='busybox:latest', command=["/bin/sleep", "300"])
volume_container_2 = Container.create(
self.client,
image='busybox:latest',
command=["top"],
labels={LABEL_PROJECT: 'composetest'},
)
host_service = self.create_service('host', volumes_from=[volume_service, volume_container_2])
host_container = host_service.create_container()
host_service.start_container(host_container)
@@ -153,60 +307,68 @@ class ServiceTest(DockerClientTestCase):
self.assertIn(volume_container_2.id,
host_container.get('HostConfig.VolumesFrom'))
def test_recreate_containers(self):
def test_execute_convergence_plan_recreate(self):
service = self.create_service(
'db',
environment={'FOO': '1'},
volumes=['/etc'],
entrypoint=['sleep'],
command=['300']
entrypoint=['top'],
command=['-d', '1']
)
old_container = service.create_container()
self.assertEqual(old_container.dictionary['Config']['Entrypoint'], ['sleep'])
self.assertEqual(old_container.dictionary['Config']['Cmd'], ['300'])
self.assertIn('FOO=1', old_container.dictionary['Config']['Env'])
self.assertEqual(old_container.get('Config.Entrypoint'), ['top'])
self.assertEqual(old_container.get('Config.Cmd'), ['-d', '1'])
self.assertIn('FOO=1', old_container.get('Config.Env'))
self.assertEqual(old_container.name, 'composetest_db_1')
service.start_container(old_container)
volume_path = old_container.inspect()['Volumes']['/etc']
old_container.inspect() # reload volume data
volume_path = old_container.get('Volumes')['/etc']
num_containers_before = len(self.client.containers(all=True))
service.options['environment']['FOO'] = '2'
tuples = service.recreate_containers()
self.assertEqual(len(tuples), 1)
new_container, = service.execute_convergence_plan(
ConvergencePlan('recreate', [old_container]))
intermediate_container = tuples[0][0]
new_container = tuples[0][1]
self.assertEqual(intermediate_container.dictionary['Config']['Entrypoint'], ['/bin/echo'])
self.assertEqual(new_container.dictionary['Config']['Entrypoint'], ['sleep'])
self.assertEqual(new_container.dictionary['Config']['Cmd'], ['300'])
self.assertIn('FOO=2', new_container.dictionary['Config']['Env'])
self.assertEqual(new_container.get('Config.Entrypoint'), ['top'])
self.assertEqual(new_container.get('Config.Cmd'), ['-d', '1'])
self.assertIn('FOO=2', new_container.get('Config.Env'))
self.assertEqual(new_container.name, 'composetest_db_1')
self.assertEqual(new_container.inspect()['Volumes']['/etc'], volume_path)
self.assertIn(intermediate_container.id, new_container.dictionary['HostConfig']['VolumesFrom'])
self.assertEqual(new_container.get('Volumes')['/etc'], volume_path)
self.assertIn(
'affinity:container==%s' % old_container.id,
new_container.get('Config.Env'))
self.assertEqual(len(self.client.containers(all=True)), num_containers_before)
self.assertNotEqual(old_container.id, new_container.id)
self.assertRaises(APIError,
self.client.inspect_container,
intermediate_container.id)
old_container.id)
def test_recreate_containers_when_containers_are_stopped(self):
def test_execute_convergence_plan_when_containers_are_stopped(self):
service = self.create_service(
'db',
environment={'FOO': '1'},
volumes=['/var/db'],
entrypoint=['sleep'],
command=['300']
entrypoint=['top'],
command=['-d', '1']
)
old_container = service.create_container()
self.assertEqual(len(service.containers(stopped=True)), 1)
service.recreate_containers()
self.assertEqual(len(service.containers(stopped=True)), 1)
service.create_container()
containers = service.containers(stopped=True)
self.assertEqual(len(containers), 1)
container, = containers
self.assertFalse(container.is_running)
def test_recreate_containers_with_image_declared_volume(self):
service.execute_convergence_plan(ConvergencePlan('start', [container]))
containers = service.containers()
self.assertEqual(len(containers), 1)
container.inspect()
self.assertEqual(container, containers[0])
self.assertTrue(container.is_running)
def test_execute_convergence_plan_with_image_declared_volume(self):
service = Service(
project='composetest',
name='db',
@@ -218,9 +380,8 @@ class ServiceTest(DockerClientTestCase):
self.assertEqual(old_container.get('Volumes').keys(), ['/data'])
volume_path = old_container.get('Volumes')['/data']
service.recreate_containers()
new_container = service.containers()[0]
service.start_container(new_container)
new_container, = service.execute_convergence_plan(
ConvergencePlan('recreate', [old_container]))
self.assertEqual(new_container.get('Volumes').keys(), ['/data'])
self.assertEqual(new_container.get('Volumes')['/data'], volume_path)
@@ -247,8 +408,7 @@ class ServiceTest(DockerClientTestCase):
set([
'composetest_db_1', 'db_1',
'composetest_db_2', 'db_2',
'db',
]),
'db'])
)
def test_start_container_creates_links_with_names(self):
@@ -264,8 +424,7 @@ class ServiceTest(DockerClientTestCase):
set([
'composetest_db_1', 'db_1',
'composetest_db_2', 'db_2',
'custom_link_name',
]),
'custom_link_name'])
)
def test_start_container_with_external_links(self):
@@ -283,8 +442,7 @@ class ServiceTest(DockerClientTestCase):
set([
'composetest_db_1',
'composetest_db_2',
'db_3',
]),
'db_3']),
)
def test_start_normal_container_does_not_create_links_to_its_own_service(self):
@@ -309,8 +467,7 @@ class ServiceTest(DockerClientTestCase):
set([
'composetest_db_1', 'db_1',
'composetest_db_2', 'db_2',
'db',
]),
'db'])
)
def test_start_container_builds_images(self):
@@ -326,7 +483,7 @@ class ServiceTest(DockerClientTestCase):
self.assertEqual(len(self.client.images(name='composetest_test')), 1)
def test_start_container_uses_tagged_image_if_it_exists(self):
self.client.build('tests/fixtures/simple-dockerfile', tag='composetest_test')
self.check_build('tests/fixtures/simple-dockerfile', tag='composetest_test')
service = Service(
name='test',
client=self.client,
@@ -343,13 +500,36 @@ class ServiceTest(DockerClientTestCase):
self.assertEqual(list(container['NetworkSettings']['Ports'].keys()), ['8000/tcp'])
self.assertNotEqual(container['NetworkSettings']['Ports']['8000/tcp'][0]['HostPort'], '8000')
def test_build(self):
base_dir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base_dir)
with open(os.path.join(base_dir, 'Dockerfile'), 'w') as f:
f.write("FROM busybox\n")
self.create_service('web', build=base_dir).build()
self.assertEqual(len(self.client.images(name='composetest_web')), 1)
def test_build_non_ascii_filename(self):
base_dir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base_dir)
with open(os.path.join(base_dir, 'Dockerfile'), 'w') as f:
f.write("FROM busybox\n")
with open(os.path.join(base_dir, b'foo\xE2bar'), 'w') as f:
f.write("hello world\n")
self.create_service('web', build=text_type(base_dir)).build()
self.assertEqual(len(self.client.images(name='composetest_web')), 1)
def test_start_container_stays_unpriviliged(self):
service = self.create_service('web')
container = create_and_start_container(service).inspect()
self.assertEqual(container['HostConfig']['Privileged'], False)
def test_start_container_becomes_priviliged(self):
service = self.create_service('web', privileged = True)
service = self.create_service('web', privileged=True)
container = create_and_start_container(service).inspect()
self.assertEqual(container['HostConfig']['Privileged'], True)
@@ -396,6 +576,11 @@ class ServiceTest(DockerClientTestCase):
],
})
def test_create_with_image_id(self):
# Image id for the current busybox:latest
service = self.create_service('foo', image='8c2e06607696')
service.create_container()
def test_scale(self):
service = self.create_service('web')
service.scale(1)
@@ -415,9 +600,138 @@ class ServiceTest(DockerClientTestCase):
service.scale(0)
self.assertEqual(len(service.containers()), 0)
def test_scale_on_service_that_cannot_be_scaled(self):
service = self.create_service('web', ports=['8000:8000'])
self.assertRaises(CannotBeScaledError, lambda: service.scale(1))
@patch('sys.stdout', new_callable=StringIO)
def test_scale_with_stopped_containers(self, mock_stdout):
"""
Given there are some stopped containers and scale is called with a
desired number that is the same as the number of stopped containers,
test that those containers are restarted and not removed/recreated.
"""
service = self.create_service('web')
next_number = service._next_container_number()
valid_numbers = [next_number, next_number + 1]
service.create_container(number=next_number, quiet=True)
service.create_container(number=next_number + 1, quiet=True)
for container in service.containers():
self.assertFalse(container.is_running)
service.scale(2)
self.assertEqual(len(service.containers()), 2)
for container in service.containers():
self.assertTrue(container.is_running)
self.assertTrue(container.number in valid_numbers)
captured_output = mock_stdout.getvalue()
self.assertNotIn('Creating', captured_output)
self.assertIn('Starting', captured_output)
@patch('sys.stdout', new_callable=StringIO)
def test_scale_with_stopped_containers_and_needing_creation(self, mock_stdout):
"""
Given there are some stopped containers and scale is called with a
desired number that is greater than the number of stopped containers,
test that those containers are restarted and required number are created.
"""
service = self.create_service('web')
next_number = service._next_container_number()
service.create_container(number=next_number, quiet=True)
for container in service.containers():
self.assertFalse(container.is_running)
service.scale(2)
self.assertEqual(len(service.containers()), 2)
for container in service.containers():
self.assertTrue(container.is_running)
captured_output = mock_stdout.getvalue()
self.assertIn('Creating', captured_output)
self.assertIn('Starting', captured_output)
@patch('sys.stdout', new_callable=StringIO)
def test_scale_with_api_returns_errors(self, mock_stdout):
"""
Test that when scaling if the API returns an error, that error is handled
and the remaining threads continue.
"""
service = self.create_service('web')
next_number = service._next_container_number()
service.create_container(number=next_number, quiet=True)
with patch(
'compose.container.Container.create',
side_effect=APIError(message="testing", response={}, explanation="Boom")):
service.scale(3)
self.assertEqual(len(service.containers()), 1)
self.assertTrue(service.containers()[0].is_running)
self.assertIn("ERROR: for 2 Boom", mock_stdout.getvalue())
@patch('sys.stdout', new_callable=StringIO)
def test_scale_with_api_returns_unexpected_exception(self, mock_stdout):
"""
Test that when scaling if the API returns an error, that is not of type
APIError, that error is re-raised.
"""
service = self.create_service('web')
next_number = service._next_container_number()
service.create_container(number=next_number, quiet=True)
with patch(
'compose.container.Container.create',
side_effect=ValueError("BOOM")):
with self.assertRaises(ValueError):
service.scale(3)
self.assertEqual(len(service.containers()), 1)
self.assertTrue(service.containers()[0].is_running)
@patch('compose.service.log')
def test_scale_with_desired_number_already_achieved(self, mock_log):
"""
Test that calling scale with a desired number that is equal to the
number of containers already running results in no change.
"""
service = self.create_service('web')
next_number = service._next_container_number()
container = service.create_container(number=next_number, quiet=True)
container.start()
self.assertTrue(container.is_running)
self.assertEqual(len(service.containers()), 1)
service.scale(1)
self.assertEqual(len(service.containers()), 1)
container.inspect()
self.assertTrue(container.is_running)
captured_output = mock_log.info.call_args[0]
self.assertIn('Desired container number already achieved', captured_output)
@patch('compose.service.log')
def test_scale_with_custom_container_name_outputs_warning(self, mock_log):
"""
Test that calling scale on a service that has a custom container name
results in warning output.
"""
service = self.create_service('web', container_name='custom-container')
self.assertEqual(service.custom_container_name(), 'custom-container')
service.scale(3)
captured_output = mock_log.warn.call_args[0][0]
self.assertEqual(len(service.containers()), 1)
self.assertIn(
"Remove the custom name to scale the service.",
captured_output
)
def test_scale_sets_ports(self):
service = self.create_service('web', ports=['8000'])
@@ -428,20 +742,30 @@ class ServiceTest(DockerClientTestCase):
self.assertEqual(list(container.inspect()['HostConfig']['PortBindings'].keys()), ['8000/tcp'])
def test_network_mode_none(self):
service = self.create_service('web', net='none')
service = self.create_service('web', net=Net('none'))
container = create_and_start_container(service)
self.assertEqual(container.get('HostConfig.NetworkMode'), 'none')
def test_network_mode_bridged(self):
service = self.create_service('web', net='bridge')
service = self.create_service('web', net=Net('bridge'))
container = create_and_start_container(service)
self.assertEqual(container.get('HostConfig.NetworkMode'), 'bridge')
def test_network_mode_host(self):
service = self.create_service('web', net='host')
service = self.create_service('web', net=Net('host'))
container = create_and_start_container(service)
self.assertEqual(container.get('HostConfig.NetworkMode'), 'host')
def test_pid_mode_none_defined(self):
service = self.create_service('web', pid=None)
container = create_and_start_container(service)
self.assertEqual(container.get('HostConfig.PidMode'), '')
def test_pid_mode_host(self):
service = self.create_service('web', pid='host')
container = create_and_start_container(service)
self.assertEqual(container.get('HostConfig.PidMode'), 'host')
def test_dns_no_value(self):
service = self.create_service('web')
container = create_and_start_container(service)
@@ -501,21 +825,116 @@ class ServiceTest(DockerClientTestCase):
def test_split_env(self):
service = self.create_service('web', environment=['NORMAL=F1', 'CONTAINS_EQUALS=F=2', 'TRAILING_EQUALS='])
env = create_and_start_container(service).environment
for k,v in {'NORMAL': 'F1', 'CONTAINS_EQUALS': 'F=2', 'TRAILING_EQUALS': ''}.items():
for k, v in {'NORMAL': 'F1', 'CONTAINS_EQUALS': 'F=2', 'TRAILING_EQUALS': ''}.items():
self.assertEqual(env[k], v)
def test_env_from_file_combined_with_env(self):
service = self.create_service('web', environment=['ONE=1', 'TWO=2', 'THREE=3'], env_file=['tests/fixtures/env/one.env', 'tests/fixtures/env/two.env'])
env = create_and_start_container(service).environment
for k,v in {'ONE': '1', 'TWO': '2', 'THREE': '3', 'FOO': 'baz', 'DOO': 'dah'}.items():
for k, v in {'ONE': '1', 'TWO': '2', 'THREE': '3', 'FOO': 'baz', 'DOO': 'dah'}.items():
self.assertEqual(env[k], v)
@mock.patch.dict(os.environ)
@patch.dict(os.environ)
def test_resolve_env(self):
os.environ['FILE_DEF'] = 'E1'
os.environ['FILE_DEF_EMPTY'] = 'E2'
os.environ['ENV_DEF'] = 'E3'
service = self.create_service('web', environment={'FILE_DEF': 'F1', 'FILE_DEF_EMPTY': '', 'ENV_DEF': None, 'NO_DEF': None})
env = create_and_start_container(service).environment
for k,v in {'FILE_DEF': 'F1', 'FILE_DEF_EMPTY': '', 'ENV_DEF': 'E3', 'NO_DEF': ''}.items():
for k, v in {'FILE_DEF': 'F1', 'FILE_DEF_EMPTY': '', 'ENV_DEF': 'E3', 'NO_DEF': ''}.items():
self.assertEqual(env[k], v)
def test_labels(self):
labels_dict = {
'com.example.description': "Accounting webapp",
'com.example.department': "Finance",
'com.example.label-with-empty-value': "",
}
compose_labels = {
LABEL_CONTAINER_NUMBER: '1',
LABEL_ONE_OFF: 'False',
LABEL_PROJECT: 'composetest',
LABEL_SERVICE: 'web',
LABEL_VERSION: __version__,
}
expected = dict(labels_dict, **compose_labels)
service = self.create_service('web', labels=labels_dict)
labels = create_and_start_container(service).labels.items()
for pair in expected.items():
self.assertIn(pair, labels)
service.kill()
service.remove_stopped()
labels_list = ["%s=%s" % pair for pair in labels_dict.items()]
service = self.create_service('web', labels=labels_list)
labels = create_and_start_container(service).labels.items()
for pair in expected.items():
self.assertIn(pair, labels)
def test_empty_labels(self):
labels_list = ['foo', 'bar']
service = self.create_service('web', labels=labels_list)
labels = create_and_start_container(service).labels.items()
for name in labels_list:
self.assertIn((name, ''), labels)
def test_custom_container_name(self):
service = self.create_service('web', container_name='my-web-container')
self.assertEqual(service.custom_container_name(), 'my-web-container')
container = create_and_start_container(service)
self.assertEqual(container.name, 'my-web-container')
one_off_container = service.create_container(one_off=True)
self.assertNotEqual(one_off_container.name, 'my-web-container')
def test_log_drive_invalid(self):
service = self.create_service('web', log_driver='xxx')
self.assertRaises(ValueError, lambda: create_and_start_container(service))
def test_log_drive_empty_default_jsonfile(self):
service = self.create_service('web')
log_config = create_and_start_container(service).log_config
self.assertEqual('json-file', log_config['Type'])
self.assertFalse(log_config['Config'])
def test_log_drive_none(self):
service = self.create_service('web', log_driver='none')
log_config = create_and_start_container(service).log_config
self.assertEqual('none', log_config['Type'])
self.assertFalse(log_config['Config'])
def test_devices(self):
service = self.create_service('web', devices=["/dev/random:/dev/mapped-random"])
device_config = create_and_start_container(service).get('HostConfig.Devices')
device_dict = {
'PathOnHost': '/dev/random',
'CgroupPermissions': 'rwm',
'PathInContainer': '/dev/mapped-random'
}
self.assertEqual(1, len(device_config))
self.assertDictEqual(device_dict, device_config[0])
def test_duplicate_containers(self):
service = self.create_service('web')
options = service._get_container_create_options({}, 1)
original = Container.create(service.client, **options)
self.assertEqual(set(service.containers(stopped=True)), set([original]))
self.assertEqual(set(service.duplicate_containers()), set())
options['name'] = 'temporary_container_name'
duplicate = Container.create(service.client, **options)
self.assertEqual(set(service.containers(stopped=True)), set([original, duplicate]))
self.assertEqual(set(service.duplicate_containers()), set([duplicate]))

View File

@@ -0,0 +1,314 @@
"""
Integration tests which cover state convergence (aka smart recreate) performed
by `docker-compose up`.
"""
from __future__ import unicode_literals
import tempfile
import shutil
import os
from compose import config
from compose.project import Project
from compose.const import LABEL_CONFIG_HASH
from .testcases import DockerClientTestCase
class ProjectTestCase(DockerClientTestCase):
def run_up(self, cfg, **kwargs):
kwargs.setdefault('timeout', 1)
project = self.make_project(cfg)
project.up(**kwargs)
return set(project.containers(stopped=True))
def make_project(self, cfg):
return Project.from_dicts(
name='composetest',
client=self.client,
service_dicts=config.load(config.ConfigDetails(cfg, 'working_dir', None))
)
class BasicProjectTest(ProjectTestCase):
def setUp(self):
super(BasicProjectTest, self).setUp()
self.cfg = {
'db': {'image': 'busybox:latest'},
'web': {'image': 'busybox:latest'},
}
def test_no_change(self):
old_containers = self.run_up(self.cfg)
self.assertEqual(len(old_containers), 2)
new_containers = self.run_up(self.cfg)
self.assertEqual(len(new_containers), 2)
self.assertEqual(old_containers, new_containers)
def test_partial_change(self):
old_containers = self.run_up(self.cfg)
old_db = [c for c in old_containers if c.name_without_project == 'db_1'][0]
old_web = [c for c in old_containers if c.name_without_project == 'web_1'][0]
self.cfg['web']['command'] = '/bin/true'
new_containers = self.run_up(self.cfg)
self.assertEqual(len(new_containers), 2)
preserved = list(old_containers & new_containers)
self.assertEqual(preserved, [old_db])
removed = list(old_containers - new_containers)
self.assertEqual(removed, [old_web])
created = list(new_containers - old_containers)
self.assertEqual(len(created), 1)
self.assertEqual(created[0].name_without_project, 'web_1')
self.assertEqual(created[0].get('Config.Cmd'), ['/bin/true'])
def test_all_change(self):
old_containers = self.run_up(self.cfg)
self.assertEqual(len(old_containers), 2)
self.cfg['web']['command'] = '/bin/true'
self.cfg['db']['command'] = '/bin/true'
new_containers = self.run_up(self.cfg)
self.assertEqual(len(new_containers), 2)
unchanged = old_containers & new_containers
self.assertEqual(len(unchanged), 0)
new = new_containers - old_containers
self.assertEqual(len(new), 2)
class ProjectWithDependenciesTest(ProjectTestCase):
def setUp(self):
super(ProjectWithDependenciesTest, self).setUp()
self.cfg = {
'db': {
'image': 'busybox:latest',
'command': 'tail -f /dev/null',
},
'web': {
'image': 'busybox:latest',
'command': 'tail -f /dev/null',
'links': ['db'],
},
'nginx': {
'image': 'busybox:latest',
'command': 'tail -f /dev/null',
'links': ['web'],
},
}
def test_up(self):
containers = self.run_up(self.cfg)
self.assertEqual(
set(c.name_without_project for c in containers),
set(['db_1', 'web_1', 'nginx_1']),
)
def test_change_leaf(self):
old_containers = self.run_up(self.cfg)
self.cfg['nginx']['environment'] = {'NEW_VAR': '1'}
new_containers = self.run_up(self.cfg)
self.assertEqual(
set(c.name_without_project for c in new_containers - old_containers),
set(['nginx_1']),
)
def test_change_middle(self):
old_containers = self.run_up(self.cfg)
self.cfg['web']['environment'] = {'NEW_VAR': '1'}
new_containers = self.run_up(self.cfg)
self.assertEqual(
set(c.name_without_project for c in new_containers - old_containers),
set(['web_1', 'nginx_1']),
)
def test_change_root(self):
old_containers = self.run_up(self.cfg)
self.cfg['db']['environment'] = {'NEW_VAR': '1'}
new_containers = self.run_up(self.cfg)
self.assertEqual(
set(c.name_without_project for c in new_containers - old_containers),
set(['db_1', 'web_1', 'nginx_1']),
)
def test_change_root_no_recreate(self):
old_containers = self.run_up(self.cfg)
self.cfg['db']['environment'] = {'NEW_VAR': '1'}
new_containers = self.run_up(self.cfg, allow_recreate=False)
self.assertEqual(new_containers - old_containers, set())
def test_service_removed_while_down(self):
next_cfg = {
'web': {
'image': 'busybox:latest',
'command': 'tail -f /dev/null',
},
'nginx': self.cfg['nginx'],
}
containers = self.run_up(self.cfg)
self.assertEqual(len(containers), 3)
project = self.make_project(self.cfg)
project.stop(timeout=1)
containers = self.run_up(next_cfg)
self.assertEqual(len(containers), 2)
def converge(service,
allow_recreate=True,
force_recreate=False,
do_build=True):
"""
If a container for this service doesn't exist, create and start one. If there are
any, stop them, create+start new ones, and remove the old containers.
"""
plan = service.convergence_plan(
allow_recreate=allow_recreate,
force_recreate=force_recreate,
)
return service.execute_convergence_plan(
plan,
do_build=do_build,
timeout=1,
)
class ServiceStateTest(DockerClientTestCase):
"""Test cases for Service.convergence_plan."""
def test_trigger_create(self):
web = self.create_service('web')
self.assertEqual(('create', []), web.convergence_plan())
def test_trigger_noop(self):
web = self.create_service('web')
container = web.create_container()
web.start()
web = self.create_service('web')
self.assertEqual(('noop', [container]), web.convergence_plan())
def test_trigger_start(self):
options = dict(command=["top"])
web = self.create_service('web', **options)
web.scale(2)
containers = web.containers(stopped=True)
containers[0].stop()
containers[0].inspect()
self.assertEqual([c.is_running for c in containers], [False, True])
web = self.create_service('web', **options)
self.assertEqual(
('start', containers[0:1]),
web.convergence_plan(),
)
def test_trigger_recreate_with_config_change(self):
web = self.create_service('web', command=["top"])
container = web.create_container()
web = self.create_service('web', command=["top", "-d", "1"])
self.assertEqual(('recreate', [container]), web.convergence_plan())
def test_trigger_recreate_with_nonexistent_image_tag(self):
web = self.create_service('web', image="busybox:latest")
container = web.create_container()
web = self.create_service('web', image="nonexistent-image")
self.assertEqual(('recreate', [container]), web.convergence_plan())
def test_trigger_recreate_with_image_change(self):
repo = 'composetest_myimage'
tag = 'latest'
image = '{}:{}'.format(repo, tag)
image_id = self.client.images(name='busybox')[0]['Id']
self.client.tag(image_id, repository=repo, tag=tag)
try:
web = self.create_service('web', image=image)
container = web.create_container()
# update the image
c = self.client.create_container(image, ['touch', '/hello.txt'])
self.client.commit(c, repository=repo, tag=tag)
self.client.remove_container(c)
web = self.create_service('web', image=image)
self.assertEqual(('recreate', [container]), web.convergence_plan())
finally:
self.client.remove_image(image)
def test_trigger_recreate_with_build(self):
context = tempfile.mkdtemp()
base_image = "FROM busybox\nLABEL com.docker.compose.test_image=true\n"
try:
dockerfile = os.path.join(context, 'Dockerfile')
with open(dockerfile, 'w') as f:
f.write(base_image)
web = self.create_service('web', build=context)
container = web.create_container()
with open(dockerfile, 'w') as f:
f.write(base_image + 'CMD echo hello world\n')
web.build()
web = self.create_service('web', build=context)
self.assertEqual(('recreate', [container]), web.convergence_plan())
finally:
shutil.rmtree(context)
class ConfigHashTest(DockerClientTestCase):
def test_no_config_hash_when_one_off(self):
web = self.create_service('web')
container = web.create_container(one_off=True)
self.assertNotIn(LABEL_CONFIG_HASH, container.labels)
def test_no_config_hash_when_overriding_options(self):
web = self.create_service('web')
container = web.create_container(environment={'FOO': '1'})
self.assertNotIn(LABEL_CONFIG_HASH, container.labels)
def test_config_hash_with_custom_labels(self):
web = self.create_service('web', labels={'foo': '1'})
container = converge(web)[0]
self.assertIn(LABEL_CONFIG_HASH, container.labels)
self.assertIn('foo', container.labels)
def test_config_hash_sticks_around(self):
web = self.create_service('web', command=["top"])
container = converge(web)[0]
self.assertIn(LABEL_CONFIG_HASH, container.labels)
web = self.create_service('web', command=["top", "-d", "1"])
container = converge(web)[0]
self.assertIn(LABEL_CONFIG_HASH, container.labels)

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